@defold-typescript/types 0.51.0 → 0.53.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 +4 -58
- package/api-signatures.json +2804 -3172
- package/api-targets.json +15 -5
- package/examples/translations.json +19 -71
- package/generated/b2d.d.ts +39 -39
- package/generated/b2d_body.d.ts +11 -11
- package/generated/b2d_chain.d.ts +3 -3
- package/generated/b2d_fixture.d.ts +5 -5
- package/generated/b2d_joint.d.ts +29 -29
- package/generated/b2d_shape.d.ts +13 -13
- package/generated/b2d_world.d.ts +15 -15
- package/generated/buffer.d.ts +10 -11
- package/generated/bullet3d.d.ts +6 -6
- package/generated/bullet3d_collision_object.d.ts +10 -10
- package/generated/bullet3d_constraint.d.ts +173 -71
- package/generated/bullet3d_rigid_body.d.ts +3 -3
- package/generated/bullet3d_shape.d.ts +11 -11
- package/generated/bullet3d_world.d.ts +20 -20
- package/generated/camera.d.ts +4 -4
- package/generated/collectionfactory.d.ts +2 -3
- package/generated/collectionproxy.d.ts +5 -5
- package/generated/compute.d.ts +5 -5
- package/generated/crash.d.ts +7 -7
- package/generated/editor-vm/http.d.ts +13 -13
- package/generated/editor-vm/image.d.ts +4 -4
- package/generated/editor-vm/json.d.ts +1 -1
- package/generated/editor-vm/tilemap_tiles.d.ts +9 -9
- package/generated/editor-vm/zip.d.ts +4 -4
- package/generated/editor.d.ts +183 -183
- package/generated/factory.d.ts +2 -3
- package/generated/font.d.ts +4 -4
- package/generated/go.d.ts +122 -230
- package/generated/graphics.d.ts +23 -25
- package/generated/gui.d.ts +186 -423
- package/generated/http.d.ts +3 -3
- package/generated/image.d.ts +63 -11
- package/generated/json.d.ts +4 -4
- package/generated/kinds/gui-script.d.ts +2 -0
- package/generated/kinds/render-script.d.ts +14 -15
- package/generated/label.d.ts +5 -5
- package/generated/liveupdate.d.ts +7 -3
- package/generated/material.d.ts +49 -49
- package/generated/model.d.ts +46 -14
- package/generated/particlefx.d.ts +24 -5
- package/generated/physics.d.ts +64 -20
- package/generated/profiler.d.ts +3 -5
- package/generated/render-script-modules/b2d.d.ts +711 -0
- package/generated/render-script-modules/bullet3d.d.ts +73 -0
- package/generated/render-script-modules/collectionfactory.d.ts +24 -0
- package/generated/render-script-modules/collectionproxy.d.ts +39 -0
- package/generated/render-script-modules/factory.d.ts +24 -0
- package/generated/render-script-modules/go.d.ts +434 -0
- package/generated/render-script-modules/label.d.ts +105 -0
- package/generated/render-script-modules/model.d.ts +121 -0
- package/generated/render-script-modules/particlefx.d.ts +59 -0
- package/generated/render-script-modules/physics.d.ts +333 -0
- package/generated/render-script-modules/resource.d.ts +1232 -0
- package/generated/render-script-modules/sound.d.ts +227 -0
- package/generated/render-script-modules/sprite.d.ts +68 -0
- package/generated/render-script-modules/tilemap.d.ts +43 -0
- package/generated/render.d.ts +172 -47
- package/generated/resource.d.ts +81 -61
- package/generated/socket.d.ts +18 -23
- package/generated/sound.d.ts +6 -6
- package/generated/sprite.d.ts +51 -3
- package/generated/sys.d.ts +87 -24
- package/generated/tilemap.d.ts +3 -3
- package/generated/timer.d.ts +3 -3
- package/generated/versions/defold-1.12.4/b2d.d.ts +2 -2
- package/generated/versions/defold-1.12.4/b2d_body.d.ts +2 -2
- package/generated/versions/defold-1.12.4/buffer.d.ts +8 -8
- package/generated/versions/defold-1.12.4/go.d.ts +117 -345
- package/generated/versions/defold-1.12.4/graphics.d.ts +2 -1
- package/generated/versions/defold-1.12.4/gui.d.ts +169 -532
- package/generated/versions/defold-1.12.4/http.d.ts +2 -2
- package/generated/versions/defold-1.12.4/image.d.ts +82 -2
- package/generated/versions/defold-1.12.4/index.d.ts +1 -0
- package/generated/versions/defold-1.12.4/label.d.ts +1 -1
- package/generated/versions/defold-1.12.4/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.12.4/model.d.ts +42 -10
- package/generated/versions/defold-1.12.4/particlefx.d.ts +23 -2
- package/generated/versions/defold-1.12.4/physics.d.ts +46 -2
- package/generated/versions/defold-1.12.4/render.d.ts +83 -7
- package/generated/versions/defold-1.12.4/resource.d.ts +50 -17
- package/generated/versions/defold-1.12.4/socket.d.ts +12 -12
- package/generated/versions/defold-1.12.4/sound.d.ts +1 -1
- package/generated/versions/defold-1.12.4/sprite.d.ts +59 -2
- package/generated/versions/defold-1.12.4/sys.d.ts +103 -8
- package/generated/versions/defold-1.12.4/tilemap.d.ts +1 -1
- package/generated/versions/defold-1.12.4/vmath.d.ts +0 -24
- package/generated/versions/defold-1.12.4/window.d.ts +3 -4
- package/generated/versions/defold-1.13.0/b2d.d.ts +2 -2
- package/generated/versions/defold-1.13.0/b2d_body.d.ts +2 -2
- package/generated/versions/defold-1.13.0/b2d_chain.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_joint.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_shape.d.ts +1 -1
- package/generated/versions/defold-1.13.0/buffer.d.ts +8 -8
- package/generated/versions/defold-1.13.0/camera.d.ts +1 -1
- package/generated/versions/defold-1.13.0/go.d.ts +117 -372
- package/generated/versions/defold-1.13.0/graphics.d.ts +2 -1
- package/generated/versions/defold-1.13.0/gui.d.ts +169 -532
- package/generated/versions/defold-1.13.0/http.d.ts +2 -2
- package/generated/versions/defold-1.13.0/image.d.ts +82 -2
- package/generated/versions/defold-1.13.0/index.d.ts +1 -0
- package/generated/versions/defold-1.13.0/label.d.ts +1 -1
- package/generated/versions/defold-1.13.0/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.13.0/material.d.ts +3 -1
- package/generated/versions/defold-1.13.0/model.d.ts +42 -10
- package/generated/versions/defold-1.13.0/particlefx.d.ts +23 -2
- package/generated/versions/defold-1.13.0/physics.d.ts +46 -2
- package/generated/versions/defold-1.13.0/render.d.ts +147 -9
- package/generated/versions/defold-1.13.0/resource.d.ts +50 -17
- package/generated/versions/defold-1.13.0/socket.d.ts +12 -12
- package/generated/versions/defold-1.13.0/sound.d.ts +1 -1
- package/generated/versions/defold-1.13.0/sprite.d.ts +59 -2
- package/generated/versions/defold-1.13.0/sys.d.ts +103 -8
- package/generated/versions/defold-1.13.0/tilemap.d.ts +1 -1
- package/generated/versions/defold-1.13.0/vmath.d.ts +0 -24
- package/generated/versions/defold-1.13.0/window.d.ts +3 -4
- package/generated/versions/defold-1.13.1/b2d.d.ts +2 -2
- package/generated/versions/defold-1.13.1/b2d_body.d.ts +2 -2
- package/generated/versions/defold-1.13.1/b2d_chain.d.ts +1 -1
- package/generated/versions/defold-1.13.1/b2d_joint.d.ts +1 -1
- package/generated/versions/defold-1.13.1/b2d_shape.d.ts +1 -1
- package/generated/versions/defold-1.13.1/buffer.d.ts +8 -8
- package/generated/versions/defold-1.13.1/camera.d.ts +1 -1
- package/generated/versions/defold-1.13.1/go.d.ts +117 -372
- package/generated/versions/defold-1.13.1/graphics.d.ts +2 -1
- package/generated/versions/defold-1.13.1/gui.d.ts +169 -548
- package/generated/versions/defold-1.13.1/http.d.ts +2 -2
- package/generated/versions/defold-1.13.1/image.d.ts +82 -2
- package/generated/versions/defold-1.13.1/index.d.ts +2 -0
- package/generated/versions/defold-1.13.1/label.d.ts +1 -1
- package/generated/versions/defold-1.13.1/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.13.1/material.d.ts +3 -1
- package/generated/versions/defold-1.13.1/model.d.ts +42 -10
- package/generated/versions/defold-1.13.1/particlefx.d.ts +23 -2
- package/generated/versions/defold-1.13.1/physics.d.ts +46 -2
- package/generated/versions/defold-1.13.1/render.d.ts +147 -9
- package/generated/versions/defold-1.13.1/resource.d.ts +50 -17
- package/generated/versions/defold-1.13.1/socket.d.ts +12 -12
- package/generated/versions/defold-1.13.1/sound.d.ts +1 -1
- package/generated/versions/defold-1.13.1/sprite.d.ts +59 -2
- package/generated/versions/defold-1.13.1/sys.d.ts +103 -8
- package/generated/versions/defold-1.13.1/tilemap.d.ts +1 -1
- package/generated/versions/defold-1.13.1/vmath.d.ts +0 -24
- package/generated/versions/defold-1.13.1/window.d.ts +3 -4
- package/generated/versions/defold-1.13.2/b2d.d.ts +39 -39
- package/generated/versions/defold-1.13.2/b2d_body.d.ts +11 -11
- package/generated/versions/defold-1.13.2/b2d_chain.d.ts +3 -3
- package/generated/versions/defold-1.13.2/b2d_fixture.d.ts +5 -5
- package/generated/versions/defold-1.13.2/b2d_joint.d.ts +29 -29
- package/generated/versions/defold-1.13.2/b2d_shape.d.ts +13 -13
- package/generated/versions/defold-1.13.2/b2d_world.d.ts +15 -15
- package/generated/versions/defold-1.13.2/buffer.d.ts +10 -11
- package/generated/versions/defold-1.13.2/bullet3d.d.ts +6 -6
- package/generated/versions/defold-1.13.2/bullet3d_collision_object.d.ts +10 -10
- package/generated/versions/defold-1.13.2/bullet3d_constraint.d.ts +173 -71
- package/generated/versions/defold-1.13.2/bullet3d_rigid_body.d.ts +3 -3
- package/generated/versions/defold-1.13.2/bullet3d_shape.d.ts +11 -11
- package/generated/versions/defold-1.13.2/bullet3d_world.d.ts +20 -20
- package/generated/versions/defold-1.13.2/camera.d.ts +4 -4
- package/generated/versions/defold-1.13.2/collectionfactory.d.ts +2 -3
- package/generated/versions/defold-1.13.2/collectionproxy.d.ts +5 -5
- package/generated/versions/defold-1.13.2/compute.d.ts +5 -5
- package/generated/versions/defold-1.13.2/crash.d.ts +7 -7
- package/generated/versions/defold-1.13.2/editor-vm/http.d.ts +13 -13
- package/generated/versions/defold-1.13.2/editor-vm/image.d.ts +4 -4
- package/generated/versions/defold-1.13.2/editor-vm/json.d.ts +1 -1
- package/generated/versions/defold-1.13.2/editor-vm/tilemap_tiles.d.ts +9 -9
- package/generated/versions/defold-1.13.2/editor-vm/zip.d.ts +4 -4
- package/generated/versions/defold-1.13.2/editor.d.ts +183 -183
- package/generated/versions/defold-1.13.2/factory.d.ts +2 -3
- package/generated/versions/defold-1.13.2/font.d.ts +4 -4
- package/generated/versions/defold-1.13.2/go.d.ts +122 -230
- package/generated/versions/defold-1.13.2/graphics.d.ts +22 -24
- package/generated/versions/defold-1.13.2/gui.d.ts +186 -423
- package/generated/versions/defold-1.13.2/http.d.ts +3 -3
- package/generated/versions/defold-1.13.2/image.d.ts +63 -11
- package/generated/versions/defold-1.13.2/index.d.ts +2 -0
- package/generated/versions/defold-1.13.2/json.d.ts +4 -4
- package/generated/versions/defold-1.13.2/label.d.ts +5 -5
- package/generated/versions/defold-1.13.2/liveupdate.d.ts +7 -3
- package/generated/versions/defold-1.13.2/material.d.ts +49 -49
- package/generated/versions/defold-1.13.2/model.d.ts +46 -14
- package/generated/versions/defold-1.13.2/particlefx.d.ts +24 -5
- package/generated/versions/defold-1.13.2/physics.d.ts +64 -20
- package/generated/versions/defold-1.13.2/profiler.d.ts +3 -5
- package/generated/versions/defold-1.13.2/render.d.ts +161 -46
- package/generated/versions/defold-1.13.2/resource.d.ts +81 -61
- package/generated/versions/defold-1.13.2/socket.d.ts +18 -23
- package/generated/versions/defold-1.13.2/sound.d.ts +6 -6
- package/generated/versions/defold-1.13.2/sprite.d.ts +51 -3
- package/generated/versions/defold-1.13.2/sys.d.ts +87 -24
- package/generated/versions/defold-1.13.2/tilemap.d.ts +3 -3
- package/generated/versions/defold-1.13.2/timer.d.ts +3 -3
- package/generated/versions/defold-1.13.2/vmath.d.ts +0 -25
- package/generated/versions/defold-1.13.2/window.d.ts +8 -10
- package/generated/vmath.d.ts +0 -25
- package/generated/window.d.ts +8 -10
- package/identity-vocabulary.json +86 -34
- package/index.d.ts +2 -0
- package/package.json +3 -1
- package/scripts/generate-api-availability.ts +10 -24
- package/scripts/generate-api-signatures.ts +75 -14
- package/scripts/materialize-version.ts +48 -7
- package/scripts/regen.ts +214 -19
- package/scripts/shared-handle-methods.ts +3 -2
- package/scripts/signature-store-fs.ts +1 -0
- package/scripts/target-version.ts +26 -0
- package/src/api-doc.ts +34 -0
- package/src/api-identity.ts +32 -1
- package/src/component-properties.d.ts +96 -7
- package/src/core-types.ts +71 -28
- package/src/doc-corrections.ts +62 -5
- package/src/emit-dts.ts +1423 -401
- package/src/engine-globals.d.ts +5 -1
- package/src/engine-type-name.ts +33 -0
- package/src/go-overloads.d.ts +74 -19
- package/src/gui-overloads.d.ts +489 -0
- package/src/gui-texture-removal.d.ts +41 -0
- package/src/index.ts +3 -0
- package/src/lifecycle.ts +32 -23
- package/src/msg-overloads.d.ts +25 -13
- package/src/signature-store.ts +50 -0
- package/src/socket-types.d.ts +3 -3
- package/src/timers.d.ts +2 -1
- package/src/vmath-overloads.d.ts +26 -0
package/src/emit-dts.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import messagesDoc from "../fixtures/messages_doc.json" with { type: "json" };
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type ApiSymbolIdentity,
|
|
4
|
+
type Box2dBackend,
|
|
5
|
+
normalizedFunctionSignature,
|
|
6
|
+
} from "./api-availability";
|
|
3
7
|
import type {
|
|
4
8
|
ApiConstant,
|
|
5
9
|
ApiFunction,
|
|
@@ -11,7 +15,7 @@ import type {
|
|
|
11
15
|
ApiTypedef,
|
|
12
16
|
ApiVariable,
|
|
13
17
|
} from "./api-doc";
|
|
14
|
-
import { DEFOLD_TYPE_MAP } from "./core-types";
|
|
18
|
+
import { DEFOLD_TYPE_MAP, SOCKET_HANDLE_NAMES, type SocketHandleReceiver } from "./core-types";
|
|
15
19
|
import { correctionPins, pinMatches, propertyCorrectionPins } from "./correction-pins";
|
|
16
20
|
import {
|
|
17
21
|
type DocCommentParts,
|
|
@@ -22,6 +26,7 @@ import {
|
|
|
22
26
|
summaryFor,
|
|
23
27
|
} from "./doc-comment";
|
|
24
28
|
import { applyMessageOverlays, parseMessagesDoc } from "./emit-messages";
|
|
29
|
+
import { engineTypeName } from "./engine-type-name";
|
|
25
30
|
import type { TranslationStore } from "./example-store";
|
|
26
31
|
import { lookupExampleSegments, translateProseFences } from "./example-store";
|
|
27
32
|
import {
|
|
@@ -36,6 +41,7 @@ import {
|
|
|
36
41
|
type LualsPosition,
|
|
37
42
|
mapLualsExpression,
|
|
38
43
|
needsArrayParens,
|
|
44
|
+
splitTopLevel,
|
|
39
45
|
} from "./luals-type-expr";
|
|
40
46
|
import { classifyUrlParameter, type UrlParameterTable } from "./url-parameters";
|
|
41
47
|
|
|
@@ -82,12 +88,20 @@ export interface EmitOptions {
|
|
|
82
88
|
// bindings (a ref-doc target), where every entry the ref-doc omits is emitted.
|
|
83
89
|
registeredConstants?: ReadonlySet<string>;
|
|
84
90
|
// The `RETURN_TYPE_CORRECTIONS` and `STRUCT_MEMBER_ADDITIONS` keys whose binding
|
|
85
|
-
// pin holds in the target's vendored engine source
|
|
86
|
-
//
|
|
87
|
-
//
|
|
91
|
+
// pin holds in the target's vendored engine source, and the
|
|
92
|
+
// `PROPERTY_KEY_RANGES` keys whose `engine` pins all hold there. A pinned
|
|
93
|
+
// entry applies only when its key is here, so a surface with no vendored
|
|
94
|
+
// bindings (absent) applies none of them.
|
|
88
95
|
bindingEvidence?: ReadonlySet<string>;
|
|
96
|
+
// The 1-based parameter slots, keyed by function FQN, that the target's engine
|
|
97
|
+
// bindings let a call omit but raise on when passed nil. Each optional one
|
|
98
|
+
// splits its function into a call without the slot and a call requiring it,
|
|
99
|
+
// so a possibly-undefined value is a compile error. Absent for a surface with
|
|
100
|
+
// no vendored bindings (a ref-doc target), which keeps every `?`.
|
|
101
|
+
nilRaisingSlots?: ReadonlyMap<string, readonly number[]>;
|
|
89
102
|
// The functions a target's skip rules removed from the module, read only to
|
|
90
|
-
// type an `OVERLOAD_SLOT_ALIASES` slot
|
|
103
|
+
// type an `OVERLOAD_SLOT_ALIASES` slot and to resolve a
|
|
104
|
+
// `CONSTANT_SLOT_RESOLUTIONS` borrow from their docs.
|
|
91
105
|
withheldFunctions?: readonly ApiFunction[];
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -179,14 +193,12 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
179
193
|
// runtime-owned passthrough: the table is filled by user code at runtime, never
|
|
180
194
|
// by Defold's API contract. factory.create/collectionfactory.create `properties`
|
|
181
195
|
// are spawn-time overrides keyed by the spawned object's own script-property
|
|
182
|
-
// names
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// mapping; each per-instance property table stays that faithful `Record`.
|
|
196
|
+
// names. No static shape exists, so `Record<string | number, unknown>` is
|
|
197
|
+
// faithful, not a loss. collectionfactory.create curates the instance-id layer
|
|
198
|
+
// of `properties` as a mapping; each per-instance property table stays that
|
|
199
|
+
// faithful `Record`.
|
|
187
200
|
"factory.create",
|
|
188
201
|
"collectionfactory.create",
|
|
189
|
-
"on_message",
|
|
190
202
|
// engine-formatted blob: the engine produces the value at runtime and no
|
|
191
203
|
// field list is documented, so the whole-slot `Record` is faithful, not a
|
|
192
204
|
// loss. crash.get_backtrace is "table containing the backtrace",
|
|
@@ -259,6 +271,11 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
259
271
|
"go.get",
|
|
260
272
|
"go.set",
|
|
261
273
|
"go.property",
|
|
274
|
+
// gui-overloads.d.ts closes the node property names and types each value per
|
|
275
|
+
// name, with a hash form for material constants.
|
|
276
|
+
"gui.animate",
|
|
277
|
+
"gui.get",
|
|
278
|
+
"gui.set",
|
|
262
279
|
// msg-overloads.d.ts supplies the typed msg.post shape.
|
|
263
280
|
"msg.post",
|
|
264
281
|
// msg-overloads.d.ts supplies the typed msg.url shape (0 / 1-string / 3-arg
|
|
@@ -277,6 +294,7 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
277
294
|
"vmath.slerp",
|
|
278
295
|
"vmath.mul_per_elem",
|
|
279
296
|
"vmath.normalize",
|
|
297
|
+
"vmath.dot",
|
|
280
298
|
// sys-overloads.d.ts supplies the disabling form and the form that takes the
|
|
281
299
|
// cooldown the engine requires when enabling.
|
|
282
300
|
"sys.set_engine_throttle",
|
|
@@ -334,13 +352,10 @@ export const MAPPING_TABLE_SLOTS: ReadonlyMap<string, { key: string; value: stri
|
|
|
334
352
|
]);
|
|
335
353
|
|
|
336
354
|
// FQN-keyed return-type overrides for functions whose doc `returnvalues` are
|
|
337
|
-
// empty yet the engine returns a value.
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
// -> unknown" rule: `gui.set` also has empty returnvalues and `void` is correct
|
|
341
|
-
// there.
|
|
355
|
+
// empty yet the engine returns a value. Per-FQN, not a blanket "empty
|
|
356
|
+
// returnvalues -> unknown" rule: most functions with empty returnvalues return
|
|
357
|
+
// nothing, and `void` is correct there.
|
|
342
358
|
export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
343
|
-
["gui.get", "unknown"],
|
|
344
359
|
// Upstream documents the returned transaction step in prose only — its
|
|
345
360
|
// `returnvalues` is empty while every sibling `editor.tx.*` builder declares
|
|
346
361
|
// one, and `editor.transact()` takes exactly that.
|
|
@@ -459,8 +474,8 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
459
474
|
slot: "code",
|
|
460
475
|
},
|
|
461
476
|
],
|
|
462
|
-
// 1.13.2
|
|
463
|
-
//
|
|
477
|
+
// 1.13.2 retypes the return to `render_predicate`, whose TYPEDEF aliases
|
|
478
|
+
// `userdata`; its type mapping emits the same brand.
|
|
464
479
|
[
|
|
465
480
|
"render.predicate",
|
|
466
481
|
{
|
|
@@ -538,6 +553,20 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
538
553
|
binding: tilemapEarlyNil("TileMap_GetTiles"),
|
|
539
554
|
},
|
|
540
555
|
],
|
|
556
|
+
[
|
|
557
|
+
"bullet3d.shape.get_shape",
|
|
558
|
+
{
|
|
559
|
+
ts: "bullet3d.shape.Definition | { type: typeof bullet3d.shape.SHAPE_TYPE_MESH }",
|
|
560
|
+
upstream: ["bullet3d.shape.definition"],
|
|
561
|
+
reason:
|
|
562
|
+
"script_bullet3d_shape.cpp:Shape_GetShape sets only `type` for a triangle mesh, which no shape definition arm matches",
|
|
563
|
+
binding: {
|
|
564
|
+
file: "gamesys/src/gamesys/scripts/bullet3d/script_bullet3d_shape.cpp",
|
|
565
|
+
cFunction: "Shape_GetShape",
|
|
566
|
+
contains: "case TRIANGLE_MESH_SHAPE_PROXYTYPE: case GIMPACT_SHAPE_PROXYTYPE: break;",
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
],
|
|
541
570
|
[
|
|
542
571
|
"collectionproxy.set_collection",
|
|
543
572
|
{
|
|
@@ -631,6 +660,36 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
631
660
|
},
|
|
632
661
|
] as const,
|
|
633
662
|
),
|
|
663
|
+
[
|
|
664
|
+
"gui.get_xanchor",
|
|
665
|
+
{
|
|
666
|
+
ts: "Exclude<gui.Anchor, typeof gui.ANCHOR_BOTTOM | typeof gui.ANCHOR_TOP>",
|
|
667
|
+
upstream: ["constant"],
|
|
668
|
+
retypedUpstream: [["gui.ANCHOR"]],
|
|
669
|
+
reason:
|
|
670
|
+
"gui_script.cpp:LuaGetXAnchor pushes GetNodeXAnchor, an x anchor: XANCHOR_NONE, XANCHOR_LEFT, or XANCHOR_RIGHT",
|
|
671
|
+
binding: {
|
|
672
|
+
file: "gui/src/gui_script.cpp",
|
|
673
|
+
cFunction: "LuaGetXAnchor",
|
|
674
|
+
contains: "lua_pushnumber(L, GetNodeXAnchor(scene, hnode));",
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
],
|
|
678
|
+
[
|
|
679
|
+
"gui.get_yanchor",
|
|
680
|
+
{
|
|
681
|
+
ts: "Exclude<gui.Anchor, typeof gui.ANCHOR_LEFT | typeof gui.ANCHOR_RIGHT>",
|
|
682
|
+
upstream: ["constant"],
|
|
683
|
+
retypedUpstream: [["gui.ANCHOR"]],
|
|
684
|
+
reason:
|
|
685
|
+
"gui_script.cpp:LuaGetYAnchor pushes GetNodeYAnchor, a y anchor: YANCHOR_NONE, YANCHOR_TOP, or YANCHOR_BOTTOM",
|
|
686
|
+
binding: {
|
|
687
|
+
file: "gui/src/gui_script.cpp",
|
|
688
|
+
cFunction: "LuaGetYAnchor",
|
|
689
|
+
contains: "lua_pushnumber(L, GetNodeYAnchor(scene, hnode));",
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
],
|
|
634
693
|
]);
|
|
635
694
|
|
|
636
695
|
// A parameter whose declared `types` disagree with what the same element's
|
|
@@ -638,23 +697,29 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
638
697
|
// target by resource name, a texture unit, a Lua sequence where `table` is
|
|
639
698
|
// declared, a hash the binding refuses as a string. Admission rule: only a slot
|
|
640
699
|
// that evidence contradicts; a slot that is merely imprecise is not a candidate.
|
|
641
|
-
//
|
|
642
|
-
// what the slot also takes and `removes` drops
|
|
643
|
-
// the
|
|
644
|
-
// optionality stays with `isDocOptional`.
|
|
645
|
-
// it contradicts, plus any later spelling
|
|
646
|
-
// `retypedUpstream`, and applies only to a slot still
|
|
647
|
-
//
|
|
648
|
-
//
|
|
649
|
-
//
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
readonly removes?: readonly string[];
|
|
700
|
+
// A correction either replaces the mapped type exactly with `ts`, or edits the
|
|
701
|
+
// mapped union: `adds` appends what the slot also takes and `removes` drops
|
|
702
|
+
// members the binding raises on. Union edits keep the URL address alias, array
|
|
703
|
+
// curation and constant mapping live; optionality stays with `isDocOptional`.
|
|
704
|
+
// Each entry records the upstream tokens it contradicts, plus any later spelling
|
|
705
|
+
// of the same defect in `retypedUpstream`, and applies only to a slot still
|
|
706
|
+
// declaring one of them. An entry with a `binding` also requires its exact
|
|
707
|
+
// `EmitOptions.bindingEvidence` key. `param-correction-provenance.test.ts` pins
|
|
708
|
+
// the ref-doc tokens against every retained target; once no target declares a
|
|
709
|
+
// pin, the entry is deleted rather than re-pinned.
|
|
710
|
+
interface ParamTypeCorrectionBase {
|
|
653
711
|
readonly upstream: readonly string[];
|
|
654
712
|
readonly retypedUpstream?: readonly (readonly string[])[];
|
|
655
713
|
readonly reason: string;
|
|
714
|
+
readonly binding?: BindingPin;
|
|
656
715
|
}
|
|
657
716
|
|
|
717
|
+
export type ParamTypeCorrection = ParamTypeCorrectionBase &
|
|
718
|
+
(
|
|
719
|
+
| { readonly ts: string; readonly adds?: undefined; readonly removes?: undefined }
|
|
720
|
+
| { readonly ts?: undefined; readonly adds?: string; readonly removes?: readonly string[] }
|
|
721
|
+
);
|
|
722
|
+
|
|
658
723
|
const CAMERA_ADDRESS_REASON =
|
|
659
724
|
"render_script_camera.cpp:CheckRenderCamera resolves anything but a number handle through ResolveURL, which takes a string or a hash";
|
|
660
725
|
|
|
@@ -688,6 +753,58 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
688
753
|
string,
|
|
689
754
|
ParamTypeCorrection
|
|
690
755
|
>([
|
|
756
|
+
[
|
|
757
|
+
"gui.set_xanchor:param:anchor",
|
|
758
|
+
{
|
|
759
|
+
ts: "Exclude<gui.Anchor, typeof gui.ANCHOR_BOTTOM | typeof gui.ANCHOR_TOP>",
|
|
760
|
+
upstream: ["constant"],
|
|
761
|
+
retypedUpstream: [["gui.ANCHOR"]],
|
|
762
|
+
reason:
|
|
763
|
+
"gui_script.cpp:LuaSetXAnchor rejects every value except XANCHOR_NONE, XANCHOR_LEFT, and XANCHOR_RIGHT",
|
|
764
|
+
binding: {
|
|
765
|
+
file: "gui/src/gui_script.cpp",
|
|
766
|
+
cFunction: "LuaSetXAnchor",
|
|
767
|
+
contains: "anchor != XANCHOR_NONE && anchor != XANCHOR_LEFT && anchor != XANCHOR_RIGHT",
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
],
|
|
771
|
+
[
|
|
772
|
+
"gui.set_yanchor:param:anchor",
|
|
773
|
+
{
|
|
774
|
+
ts: "Exclude<gui.Anchor, typeof gui.ANCHOR_LEFT | typeof gui.ANCHOR_RIGHT>",
|
|
775
|
+
upstream: ["constant"],
|
|
776
|
+
retypedUpstream: [["gui.ANCHOR"]],
|
|
777
|
+
reason:
|
|
778
|
+
"gui_script.cpp:LuaSetYAnchor rejects every value except YANCHOR_NONE, YANCHOR_TOP, and YANCHOR_BOTTOM",
|
|
779
|
+
binding: {
|
|
780
|
+
file: "gui/src/gui_script.cpp",
|
|
781
|
+
cFunction: "LuaSetYAnchor",
|
|
782
|
+
contains: "anchor != YANCHOR_NONE && anchor != YANCHOR_TOP && anchor != YANCHOR_BOTTOM",
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
],
|
|
786
|
+
[
|
|
787
|
+
"go.animate:param:playback",
|
|
788
|
+
{
|
|
789
|
+
ts: "go.Playback",
|
|
790
|
+
upstream: [
|
|
791
|
+
"go.PLAYBACK_ONCE_FORWARD",
|
|
792
|
+
"go.PLAYBACK_ONCE_BACKWARD",
|
|
793
|
+
"go.PLAYBACK_ONCE_PINGPONG",
|
|
794
|
+
"go.PLAYBACK_LOOP_FORWARD",
|
|
795
|
+
"go.PLAYBACK_LOOP_BACKWARD",
|
|
796
|
+
"go.PLAYBACK_LOOP_PINGPONG",
|
|
797
|
+
],
|
|
798
|
+
retypedUpstream: [["go.PLAYBACK"]],
|
|
799
|
+
reason:
|
|
800
|
+
"gameobject_script.cpp:Script_Animate accepts every playback value below PLAYBACK_COUNT, including PLAYBACK_NONE",
|
|
801
|
+
binding: {
|
|
802
|
+
file: "gameobject/src/gameobject/gameobject_script.cpp",
|
|
803
|
+
cFunction: "Script_Animate",
|
|
804
|
+
contains: "if (playback >= PLAYBACK_COUNT)",
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
],
|
|
691
808
|
...CAMERA_SLOT_FUNCTIONS.map(
|
|
692
809
|
(name) =>
|
|
693
810
|
[
|
|
@@ -757,6 +874,35 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
757
874
|
},
|
|
758
875
|
] as const,
|
|
759
876
|
),
|
|
877
|
+
...(
|
|
878
|
+
[
|
|
879
|
+
["clone_tree", "LuaCloneTree"],
|
|
880
|
+
["get_tree", "LuaGetTree"],
|
|
881
|
+
] as const
|
|
882
|
+
).map(
|
|
883
|
+
([name, cFunction]) =>
|
|
884
|
+
[
|
|
885
|
+
`gui.${name}:param:node`,
|
|
886
|
+
{
|
|
887
|
+
adds: "undefined",
|
|
888
|
+
upstream: ["node"],
|
|
889
|
+
reason: `gui_script.cpp:${cFunction} pushes its result table before testing argument 1 with lua_isnil, so nil selects the whole scene and an absent argument reaches LuaCheckNodeInternal and raises`,
|
|
890
|
+
},
|
|
891
|
+
] as const,
|
|
892
|
+
),
|
|
893
|
+
...(["get_slider_motor", "set_slider_motor"] as const).map(
|
|
894
|
+
(name) =>
|
|
895
|
+
[
|
|
896
|
+
`bullet3d.constraint.${name}:param:motor`,
|
|
897
|
+
{
|
|
898
|
+
adds: '"linear" | "angular"',
|
|
899
|
+
removes: ["string"],
|
|
900
|
+
upstream: ["string"],
|
|
901
|
+
reason:
|
|
902
|
+
"script_bullet3d_constraint.cpp:CheckSliderMotorAxis raises \"motor must be 'linear' or 'angular'\" on any other string",
|
|
903
|
+
},
|
|
904
|
+
] as const,
|
|
905
|
+
),
|
|
760
906
|
[
|
|
761
907
|
"sound.get_group_name:param:group",
|
|
762
908
|
{
|
|
@@ -835,9 +981,10 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
835
981
|
"render.disable_texture:param:binding",
|
|
836
982
|
{
|
|
837
983
|
adds: "number",
|
|
984
|
+
removes: ['Opaque<"texture">'],
|
|
838
985
|
upstream: ["texture", "string", "hash"],
|
|
839
986
|
reason:
|
|
840
|
-
|
|
987
|
+
"render_script.cpp:RenderScript_DisableTexture reads a number as a texture unit with lua_isnumber and anything else with CheckHashOrString; a texture handle is a number, so it is read as a unit",
|
|
841
988
|
},
|
|
842
989
|
],
|
|
843
990
|
[
|
|
@@ -855,11 +1002,42 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
855
1002
|
adds: 'Opaque<"render_predicate">',
|
|
856
1003
|
removes: ["number"],
|
|
857
1004
|
upstream: ["number"],
|
|
858
|
-
retypedUpstream: [["render_predicate"]],
|
|
859
1005
|
reason:
|
|
860
|
-
"render_script.cpp:RenderScript_Draw reads the predicate with RenderScriptPredicate_Check, the userdata render.predicate returns; 1.13.2
|
|
1006
|
+
"render_script.cpp:RenderScript_Draw reads the predicate with RenderScriptPredicate_Check, the userdata render.predicate returns; 1.13.2 types the slot `render_predicate`, whose type mapping is that brand",
|
|
861
1007
|
},
|
|
862
1008
|
],
|
|
1009
|
+
...(["enable_state", "disable_state"] as const).map(
|
|
1010
|
+
(name) =>
|
|
1011
|
+
[
|
|
1012
|
+
`render.${name}:param:state`,
|
|
1013
|
+
{
|
|
1014
|
+
adds: "Exclude<graphics.State, typeof graphics.STATE_ALPHA_TEST_SUPPORTED | typeof graphics.STATE_SCISSOR_TEST>",
|
|
1015
|
+
removes: ["graphics.State"],
|
|
1016
|
+
upstream: ["graphics.STATE"],
|
|
1017
|
+
reason: `render_script.cpp:RenderScript_${name === "enable_state" ? "Enable" : "Disable"}State raises "Invalid state" on any state but the six it lists, so graphics.STATE_SCISSOR_TEST and graphics.STATE_ALPHA_TEST_SUPPORTED are rejected`,
|
|
1018
|
+
},
|
|
1019
|
+
] as const,
|
|
1020
|
+
),
|
|
1021
|
+
...(
|
|
1022
|
+
[
|
|
1023
|
+
"source_factor_color",
|
|
1024
|
+
"destination_factor_color",
|
|
1025
|
+
"source_factor_alpha",
|
|
1026
|
+
"destination_factor_alpha",
|
|
1027
|
+
] as const
|
|
1028
|
+
).map(
|
|
1029
|
+
(slot) =>
|
|
1030
|
+
[
|
|
1031
|
+
`render.set_blend_func_separate:param:${slot}`,
|
|
1032
|
+
{
|
|
1033
|
+
adds: "graphics.BlendFactor",
|
|
1034
|
+
removes: ["number"],
|
|
1035
|
+
upstream: ["number"],
|
|
1036
|
+
reason:
|
|
1037
|
+
'render_script.cpp:RenderScript_SetBlendFuncSeparate raises "Invalid blend factor" on any value CheckBlendFactor does not list',
|
|
1038
|
+
},
|
|
1039
|
+
] as const,
|
|
1040
|
+
),
|
|
863
1041
|
[
|
|
864
1042
|
"go.delete:param:id",
|
|
865
1043
|
{
|
|
@@ -878,15 +1056,6 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
878
1056
|
reason: 'sys.load_buffer example `buffer.get_bytes(my_buffer, "data")`',
|
|
879
1057
|
},
|
|
880
1058
|
],
|
|
881
|
-
[
|
|
882
|
-
"gui.set:param:value",
|
|
883
|
-
{
|
|
884
|
-
adds: "Matrix4 | Hash",
|
|
885
|
-
upstream: ["number", "vector4", "vector3", "quaternion"],
|
|
886
|
-
reason:
|
|
887
|
-
'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',
|
|
888
|
-
},
|
|
889
|
-
],
|
|
890
1059
|
[
|
|
891
1060
|
"gui.set_rotation:param:rotation",
|
|
892
1061
|
{
|
|
@@ -918,8 +1087,8 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
918
1087
|
[
|
|
919
1088
|
"window.set_dim_mode:param:mode",
|
|
920
1089
|
{
|
|
921
|
-
removes: ["window.
|
|
922
|
-
adds: "window.
|
|
1090
|
+
removes: ["window.Dimming"],
|
|
1091
|
+
adds: "Exclude<window.Dimming, typeof window.DIMMING_UNKNOWN>",
|
|
923
1092
|
upstream: ["window.DIMMING"],
|
|
924
1093
|
reason:
|
|
925
1094
|
'script_window.cpp:SetDimMode raises "The dim mode specified is not supported." for anything but DIMMING_ON and DIMMING_OFF',
|
|
@@ -1021,16 +1190,70 @@ export const PROPERTY_TYPE_CORRECTIONS: ReadonlyMap<string, PropertyTypeCorrecti
|
|
|
1021
1190
|
// saying it, or when the phrase stops stating `first-last`. Curated rather than
|
|
1022
1191
|
// matched on a trailing `N`, so each new upstream placeholder is a deliberate
|
|
1023
1192
|
// addition.
|
|
1193
|
+
//
|
|
1194
|
+
// `engine.last` replaces `last` on a target whose vendored engine source shows
|
|
1195
|
+
// every one of `engine.pins` (the `EmitOptions.bindingEvidence` keys); every
|
|
1196
|
+
// other surface, including one generated from a ref-doc alone, keeps the range
|
|
1197
|
+
// `evidence` states.
|
|
1024
1198
|
export interface PropertyKeyRange {
|
|
1025
1199
|
readonly prefix: string;
|
|
1026
1200
|
readonly first: number;
|
|
1027
1201
|
readonly last: number;
|
|
1028
1202
|
readonly evidence: string;
|
|
1203
|
+
readonly engine?: { readonly last: number; readonly pins: readonly EngineSourcePin[] };
|
|
1029
1204
|
}
|
|
1030
1205
|
|
|
1031
|
-
//
|
|
1206
|
+
// A token snippet the target's vendored engine evidence must contain. With
|
|
1207
|
+
// `cFunction` it is matched in that function's body, without it anywhere in
|
|
1208
|
+
// the file.
|
|
1209
|
+
export interface EngineSourcePin {
|
|
1210
|
+
// Relative to the target's vendored `engine/` tree, as `BindingPin.file` is.
|
|
1211
|
+
readonly file: string;
|
|
1212
|
+
readonly cFunction?: string;
|
|
1213
|
+
readonly contains: string;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const MODEL_TEXTURE_LOOP =
|
|
1217
|
+
"for (uint32_t i = 0; i < dmRender::RenderObject::MAX_TEXTURE_COUNT; ++i) { if (params.m_PropertyId == PROP_TEXTURE[i])";
|
|
1218
|
+
|
|
1219
|
+
// Keyed `<namespace>.<property>`, mirroring PROPERTY_TYPE_CORRECTIONS; a key
|
|
1220
|
+
// with an `engine` field is also an `EmitOptions.bindingEvidence` key.
|
|
1032
1221
|
export const PROPERTY_KEY_RANGES: ReadonlyMap<string, PropertyKeyRange> = new Map([
|
|
1033
|
-
[
|
|
1222
|
+
[
|
|
1223
|
+
"model.textureN",
|
|
1224
|
+
{
|
|
1225
|
+
prefix: "texture",
|
|
1226
|
+
first: 0,
|
|
1227
|
+
last: 7,
|
|
1228
|
+
evidence: "textureN where N is 0-7",
|
|
1229
|
+
engine: {
|
|
1230
|
+
last: 15,
|
|
1231
|
+
pins: [
|
|
1232
|
+
{
|
|
1233
|
+
file: "render/src/dmsdk/render/render.h",
|
|
1234
|
+
contains: "static const uint32_t MAX_TEXTURE_COUNT = 16;",
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
file: "gamesys/src/gamesys/gamesys.cpp",
|
|
1238
|
+
contains: `const dmhash_t PROP_TEXTURE[dmRender::RenderObject::MAX_TEXTURE_COUNT] = { ${Array.from(
|
|
1239
|
+
{ length: 16 },
|
|
1240
|
+
(_, unit) => `dmHashString64("texture${unit}"),`,
|
|
1241
|
+
).join(" ")} };`,
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
file: "gamesys/src/gamesys/components/comp_model.cpp",
|
|
1245
|
+
cFunction: "CompModelGetProperty",
|
|
1246
|
+
contains: MODEL_TEXTURE_LOOP,
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
file: "gamesys/src/gamesys/components/comp_model.cpp",
|
|
1250
|
+
cFunction: "CompModelSetProperty",
|
|
1251
|
+
contains: MODEL_TEXTURE_LOOP,
|
|
1252
|
+
},
|
|
1253
|
+
],
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
],
|
|
1034
1257
|
]);
|
|
1035
1258
|
|
|
1036
1259
|
// A parameter the ref-doc metadata marks required while the same element's prose
|
|
@@ -1095,6 +1318,10 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
|
1095
1318
|
"render.set_render_target:param:render_target",
|
|
1096
1319
|
"render_script.cpp:RenderScript_SetRenderTarget sets the default target when lua_gettop(L) == 0",
|
|
1097
1320
|
],
|
|
1321
|
+
[
|
|
1322
|
+
"buffer.get_bytes:param:stream_name",
|
|
1323
|
+
"script_buffer.cpp:GetBytes reads only argument 1 with CheckBufferUnpack and returns every byte of the buffer",
|
|
1324
|
+
],
|
|
1098
1325
|
["socket.newtry:param:finalizer", "socket.protect's example `local try = socket.newtry()`"],
|
|
1099
1326
|
...(
|
|
1100
1327
|
[
|
|
@@ -1237,13 +1464,6 @@ export const REQUIRED_SLOT_CORRECTIONS: ReadonlyMap<string, RequiredSlotCorrecti
|
|
|
1237
1464
|
evidence: "gui_script.cpp:LuaSetParent reads a node from argument 2 unless lua_isnil(L, 2)",
|
|
1238
1465
|
},
|
|
1239
1466
|
],
|
|
1240
|
-
[
|
|
1241
|
-
"gui.set:param:value",
|
|
1242
|
-
{
|
|
1243
|
-
nil: true,
|
|
1244
|
-
evidence: "gui_script.cpp:LuaSet converts argument 3 with LuaToVar unless lua_isnil(L, 3)",
|
|
1245
|
-
},
|
|
1246
|
-
],
|
|
1247
1467
|
[
|
|
1248
1468
|
"window.set_listener:param:callback",
|
|
1249
1469
|
{
|
|
@@ -1333,6 +1553,335 @@ function firstSlotAlternative(
|
|
|
1333
1553
|
return original === null ? null : { ...prepared, original };
|
|
1334
1554
|
}
|
|
1335
1555
|
|
|
1556
|
+
// The calls a function splits into at its optional nil-raising slots
|
|
1557
|
+
// (`EmitOptions.nilRaisingSlots`): one ending before each such slot, then one
|
|
1558
|
+
// taking them all, every such slot before an arm's end declared required. A
|
|
1559
|
+
// required slot, or one a rest parameter folds in, splits nothing.
|
|
1560
|
+
function nilRaisingArms(
|
|
1561
|
+
prepared: PreparedFunction,
|
|
1562
|
+
nilRaisingSlots: ReadonlyMap<string, readonly number[]>,
|
|
1563
|
+
): PreparedFunction[] {
|
|
1564
|
+
const { name, parameters } = prepared.original;
|
|
1565
|
+
const firstVararg = parameters.findIndex(isVarargParameter);
|
|
1566
|
+
const positional = firstVararg === -1 ? parameters.length : firstVararg;
|
|
1567
|
+
const split = [...new Set(nilRaisingSlots.get(name) ?? [])]
|
|
1568
|
+
.map((slot) => slot - 1)
|
|
1569
|
+
.filter((index) => {
|
|
1570
|
+
const p = parameters[index];
|
|
1571
|
+
return index < positional && p !== undefined && isDocOptional(p, name);
|
|
1572
|
+
})
|
|
1573
|
+
.sort((a, b) => a - b);
|
|
1574
|
+
if (split.length === 0) return [prepared];
|
|
1575
|
+
const shorter = split.map((end, k) => ({
|
|
1576
|
+
...prepared,
|
|
1577
|
+
original: { ...prepared.original, parameters: parameters.slice(0, end) },
|
|
1578
|
+
required: new Set(split.slice(0, k)),
|
|
1579
|
+
}));
|
|
1580
|
+
return [...shorter, { ...prepared, required: new Set(split) }];
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// Table fields a binding reads as required only when `argument` is non-nil,
|
|
1584
|
+
// keyed by FQN. A curated function splits into a call whose `table` slot requires
|
|
1585
|
+
// `fields`, with `argument` as declared, then a call whose `argument` is nil, so
|
|
1586
|
+
// a possibly-nil argument must carry them.
|
|
1587
|
+
export interface PresenceRequiredFields {
|
|
1588
|
+
readonly argument: string;
|
|
1589
|
+
readonly table: string;
|
|
1590
|
+
readonly fields: readonly string[];
|
|
1591
|
+
readonly reason: string;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
const FRAME_B_FIELDS = ["frame_b_position", "frame_b_rotation"] as const;
|
|
1595
|
+
|
|
1596
|
+
export const PRESENCE_REQUIRED_FIELDS: ReadonlyMap<string, PresenceRequiredFields> = new Map<
|
|
1597
|
+
string,
|
|
1598
|
+
PresenceRequiredFields
|
|
1599
|
+
>([
|
|
1600
|
+
[
|
|
1601
|
+
"bullet3d.constraint.create_point_to_point",
|
|
1602
|
+
{
|
|
1603
|
+
argument: "body_b",
|
|
1604
|
+
table: "params",
|
|
1605
|
+
fields: ["pivot_b"],
|
|
1606
|
+
reason:
|
|
1607
|
+
"script_bullet3d_constraint.cpp:Constraint_CreatePointToPoint reads pivot_b as required when input.m_BodyB != 0",
|
|
1608
|
+
},
|
|
1609
|
+
],
|
|
1610
|
+
...(
|
|
1611
|
+
[
|
|
1612
|
+
["create_hinge", "Constraint_CreateHinge"],
|
|
1613
|
+
["create_cone_twist", "Constraint_CreateConeTwist"],
|
|
1614
|
+
["create_generic_6dof", "Constraint_CreateGeneric6Dof"],
|
|
1615
|
+
["create_slider", "Constraint_CreateSlider"],
|
|
1616
|
+
] as const
|
|
1617
|
+
).map(
|
|
1618
|
+
([name, cFunction]) =>
|
|
1619
|
+
[
|
|
1620
|
+
`bullet3d.constraint.${name}`,
|
|
1621
|
+
{
|
|
1622
|
+
argument: "body_b",
|
|
1623
|
+
table: "params",
|
|
1624
|
+
fields: FRAME_B_FIELDS,
|
|
1625
|
+
reason: `script_bullet3d_constraint.cpp:${cFunction} reads frame B as required under if (input.m_BodyB)`,
|
|
1626
|
+
},
|
|
1627
|
+
] as const,
|
|
1628
|
+
),
|
|
1629
|
+
]);
|
|
1630
|
+
|
|
1631
|
+
// The calls PRESENCE_REQUIRED_FIELDS splits `prepared` into: the declared one
|
|
1632
|
+
// with its fields required, then the one whose argument is nil.
|
|
1633
|
+
function presenceArms(
|
|
1634
|
+
prepared: PreparedFunction,
|
|
1635
|
+
structs: readonly ApiStruct[],
|
|
1636
|
+
): PreparedFunction[] {
|
|
1637
|
+
const { name, parameters } = prepared.original;
|
|
1638
|
+
const entry = PRESENCE_REQUIRED_FIELDS.get(name);
|
|
1639
|
+
if (entry === undefined) return [prepared];
|
|
1640
|
+
const argument = parameters.findIndex((p) => p.name === entry.argument);
|
|
1641
|
+
const table = parameters.findIndex((p) => p.name === entry.table);
|
|
1642
|
+
const struct = structs.find((s) => parameters[table]?.types.includes(s.name));
|
|
1643
|
+
if (argument === -1 || struct === undefined) {
|
|
1644
|
+
throw new Error(
|
|
1645
|
+
`presence-required fields ${name}: no ${entry.argument} parameter or ${entry.table} table`,
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
const missing = entry.fields.filter((field) => !struct.members.some((m) => m.name === field));
|
|
1649
|
+
if (missing.length > 0) {
|
|
1650
|
+
throw new Error(`presence-required fields ${name}: ${struct.name} lacks ${missing.join(", ")}`);
|
|
1651
|
+
}
|
|
1652
|
+
const picked = entry.fields.map((field) => `"${field}"`).join(" | ");
|
|
1653
|
+
const requireFields = (type: string): string => {
|
|
1654
|
+
const operand = /^[\w$.]+$/.test(type) ? type : `(${type})`;
|
|
1655
|
+
return `${operand} & Required<Pick<${operand}, ${picked}>>`;
|
|
1656
|
+
};
|
|
1657
|
+
const strict: PreparedFunction = { ...prepared, retype: new Map([[table, requireFields]]) };
|
|
1658
|
+
// Declared required, the nil argument emits as `undefined` alone rather than
|
|
1659
|
+
// gaining the `| undefined` of an interior optional slot.
|
|
1660
|
+
const absent: PreparedFunction = {
|
|
1661
|
+
...prepared,
|
|
1662
|
+
required: new Set([...(prepared.required ?? []), argument]),
|
|
1663
|
+
retype: new Map([[argument, () => "undefined"]]),
|
|
1664
|
+
};
|
|
1665
|
+
return [strict, absent];
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// A rule an engine binding enforces on one parameter that no ref-doc token
|
|
1669
|
+
// states, keyed `<fqn>:param:<name>`. Each rewrites the slot's mapped type, so
|
|
1670
|
+
// one entry holds on every target whatever token spelling produced that type.
|
|
1671
|
+
export type EngineSlotRule =
|
|
1672
|
+
// `E[]` becomes `[E, ...E[]]`: the binding raises on an empty list.
|
|
1673
|
+
| { readonly kind: "non-empty"; readonly reason: string }
|
|
1674
|
+
// The constants the binding raises on leave the slot's type.
|
|
1675
|
+
| { readonly kind: "excludes"; readonly constants: readonly string[]; readonly reason: string }
|
|
1676
|
+
// With `flag` true the table must carry `fields`, which the binding then reads
|
|
1677
|
+
// without a default.
|
|
1678
|
+
| {
|
|
1679
|
+
readonly kind: "flag-requires";
|
|
1680
|
+
readonly flag: string;
|
|
1681
|
+
readonly fields: readonly string[];
|
|
1682
|
+
readonly reason: string;
|
|
1683
|
+
}
|
|
1684
|
+
// In each call that requires `slot`, this slot is required too and its
|
|
1685
|
+
// top-level union loses `drops`.
|
|
1686
|
+
| {
|
|
1687
|
+
readonly kind: "alongside";
|
|
1688
|
+
readonly slot: string;
|
|
1689
|
+
readonly drops: readonly string[];
|
|
1690
|
+
readonly reason: string;
|
|
1691
|
+
};
|
|
1692
|
+
|
|
1693
|
+
export const ENGINE_SLOT_RULES: ReadonlyMap<string, EngineSlotRule> = new Map<
|
|
1694
|
+
string,
|
|
1695
|
+
EngineSlotRule
|
|
1696
|
+
>([
|
|
1697
|
+
[
|
|
1698
|
+
"buffer.create:param:declaration",
|
|
1699
|
+
{
|
|
1700
|
+
kind: "non-empty",
|
|
1701
|
+
reason:
|
|
1702
|
+
'script_buffer.cpp:Create raises "You must specify at least one stream declaration" when the list holds fewer than one entry',
|
|
1703
|
+
},
|
|
1704
|
+
],
|
|
1705
|
+
[
|
|
1706
|
+
"buffer.set_metadata:param:values",
|
|
1707
|
+
{
|
|
1708
|
+
kind: "non-empty",
|
|
1709
|
+
reason:
|
|
1710
|
+
'script_buffer.cpp:SetMetadata raises "invalid metadata" when lua_objlen of values is 0',
|
|
1711
|
+
},
|
|
1712
|
+
],
|
|
1713
|
+
[
|
|
1714
|
+
"buffer.set_metadata:param:value_type",
|
|
1715
|
+
{
|
|
1716
|
+
kind: "excludes",
|
|
1717
|
+
constants: ["buffer.VALUE_TYPE_INT64", "buffer.VALUE_TYPE_UINT64"],
|
|
1718
|
+
reason:
|
|
1719
|
+
'script_buffer.cpp:SetMetadata raises "64 bit integer metadata are not supported." on VALUE_TYPE_UINT64 and VALUE_TYPE_INT64',
|
|
1720
|
+
},
|
|
1721
|
+
],
|
|
1722
|
+
[
|
|
1723
|
+
"resource.create_sound_data:param:options",
|
|
1724
|
+
{
|
|
1725
|
+
kind: "flag-requires",
|
|
1726
|
+
flag: "partial",
|
|
1727
|
+
fields: ["filesize"],
|
|
1728
|
+
reason:
|
|
1729
|
+
"script_resource.cpp:CreateSoundData reads filesize with the no-default CheckFieldValue<int> when partial is true, and CheckValue<int> raises on a non-number",
|
|
1730
|
+
},
|
|
1731
|
+
],
|
|
1732
|
+
[
|
|
1733
|
+
"go.delete:param:id",
|
|
1734
|
+
{
|
|
1735
|
+
kind: "alongside",
|
|
1736
|
+
slot: "recursive",
|
|
1737
|
+
drops: ["boolean"],
|
|
1738
|
+
reason:
|
|
1739
|
+
"gameobject_script.cpp:Script_Delete raises on a boolean id with a second argument, and on a nil id once recursive is popped",
|
|
1740
|
+
},
|
|
1741
|
+
],
|
|
1742
|
+
]);
|
|
1743
|
+
|
|
1744
|
+
// `prepared` with `rewrite` applied after any rewrite it already makes at `index`.
|
|
1745
|
+
function withRetype(
|
|
1746
|
+
prepared: PreparedFunction,
|
|
1747
|
+
index: number,
|
|
1748
|
+
rewrite: (type: string) => string,
|
|
1749
|
+
): PreparedFunction {
|
|
1750
|
+
const retype = new Map(prepared.retype);
|
|
1751
|
+
const prior = retype.get(index);
|
|
1752
|
+
retype.set(index, prior === undefined ? rewrite : (type) => rewrite(prior(type)));
|
|
1753
|
+
return { ...prepared, retype };
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// `E[]` as `[E, ...E[]]`; the element keeps its own parentheses.
|
|
1757
|
+
function nonEmptyList(type: string, key: string): string {
|
|
1758
|
+
const element = type.endsWith("[]") ? type.slice(0, -2) : "";
|
|
1759
|
+
const single =
|
|
1760
|
+
splitTopLevel(type, "|").length === 1 &&
|
|
1761
|
+
splitTopLevel(type, "&").length === 1 &&
|
|
1762
|
+
!isFunctionTypeText(type);
|
|
1763
|
+
if (element === "" || !single) {
|
|
1764
|
+
throw new Error(`engine slot rule ${key}: ${type} is not one array`);
|
|
1765
|
+
}
|
|
1766
|
+
return `[${element}, ...${element}[]]`;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
function withoutMembers(type: string, drops: readonly string[], key: string): string {
|
|
1770
|
+
const members = splitTopLevel(type, "|").map((member) => member.trim());
|
|
1771
|
+
const missing = drops.filter((drop) => !members.includes(drop));
|
|
1772
|
+
if (missing.length > 0) {
|
|
1773
|
+
throw new Error(`engine slot rule ${key}: ${type} has no ${missing.join(", ")} to drop`);
|
|
1774
|
+
}
|
|
1775
|
+
return members.filter((member) => !drops.includes(member)).join(" | ");
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// The field names a table slot declares: its STRUCT's members, else the fields
|
|
1779
|
+
// parsed from its own doc.
|
|
1780
|
+
function slotFieldNames(parameter: ApiParameter, structs: readonly ApiStruct[]): string[] {
|
|
1781
|
+
const struct = structs.find((s) => parameter.types.includes(s.name));
|
|
1782
|
+
if (struct !== undefined) return struct.members.map((member) => member.name);
|
|
1783
|
+
const fields = parameter.fields ?? parseTableFields(parameter.doc) ?? [];
|
|
1784
|
+
return fields.map((field) => field.name);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
function engineSlotRuleArms(
|
|
1788
|
+
prepared: PreparedFunction,
|
|
1789
|
+
arms: readonly PreparedFunction[],
|
|
1790
|
+
key: string,
|
|
1791
|
+
rule: EngineSlotRule,
|
|
1792
|
+
index: number,
|
|
1793
|
+
structs: readonly ApiStruct[],
|
|
1794
|
+
bindingsRead: boolean,
|
|
1795
|
+
): PreparedFunction[] {
|
|
1796
|
+
const { name, parameters } = prepared.original;
|
|
1797
|
+
const takes = (arm: PreparedFunction): boolean => index < arm.original.parameters.length;
|
|
1798
|
+
const rewriteEach = (rewrite: (type: string) => string): PreparedFunction[] =>
|
|
1799
|
+
arms.map((arm) => (takes(arm) ? withRetype(arm, index, rewrite) : arm));
|
|
1800
|
+
switch (rule.kind) {
|
|
1801
|
+
case "non-empty":
|
|
1802
|
+
return rewriteEach((type) => nonEmptyList(type, key));
|
|
1803
|
+
case "excludes": {
|
|
1804
|
+
const excluded = rule.constants.map((constant) => `typeof ${constant}`).join(" | ");
|
|
1805
|
+
return rewriteEach((type) => `Exclude<${type}, ${excluded}>`);
|
|
1806
|
+
}
|
|
1807
|
+
case "flag-requires": {
|
|
1808
|
+
const declared = slotFieldNames(parameters[index] as ApiParameter, structs);
|
|
1809
|
+
const missing = [rule.flag, ...rule.fields].filter((field) => !declared.includes(field));
|
|
1810
|
+
if (missing.length > 0) {
|
|
1811
|
+
throw new Error(`engine slot rule ${key}: the table declares no ${missing.join(", ")}`);
|
|
1812
|
+
}
|
|
1813
|
+
const picked = rule.fields.map((field) => `"${field}"`).join(" | ");
|
|
1814
|
+
return rewriteEach((type) => {
|
|
1815
|
+
const operand =
|
|
1816
|
+
splitTopLevel(type, "|").length > 1 || isFunctionTypeText(type) ? `(${type})` : type;
|
|
1817
|
+
return `${operand} & ({ ${rule.flag}?: false } | ({ ${rule.flag}: true } & Required<Pick<${type}, ${picked}>>))`;
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
case "alongside": {
|
|
1821
|
+
const partner = parameters.findIndex((p) => p.name === rule.slot);
|
|
1822
|
+
const partnerParameter = parameters[partner];
|
|
1823
|
+
const requires = (arm: PreparedFunction): boolean =>
|
|
1824
|
+
partnerParameter !== undefined &&
|
|
1825
|
+
partner < arm.original.parameters.length &&
|
|
1826
|
+
(arm.required?.has(partner) === true || !isDocOptional(partnerParameter, name));
|
|
1827
|
+
if (!arms.some(requires)) {
|
|
1828
|
+
// Without the target's nil-raising slots no call was split out to
|
|
1829
|
+
// require the partner, so there is no binding to have stopped marking it.
|
|
1830
|
+
if (!bindingsRead) return [...arms];
|
|
1831
|
+
throw new Error(
|
|
1832
|
+
partnerParameter === undefined
|
|
1833
|
+
? `engine slot rule ${key}: ${name} has no ${rule.slot} parameter`
|
|
1834
|
+
: `engine slot rule ${key}: no call of ${name} requires ${rule.slot}`,
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
return arms.map((arm) =>
|
|
1838
|
+
requires(arm)
|
|
1839
|
+
? {
|
|
1840
|
+
...withRetype(arm, index, (type) => withoutMembers(type, rule.drops, key)),
|
|
1841
|
+
required: new Set([...(arm.required ?? []), index]),
|
|
1842
|
+
}
|
|
1843
|
+
: arm,
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// `arms` with every ENGINE_SLOT_RULES entry keyed to `prepared` applied.
|
|
1850
|
+
// `bindingsRead` says the emit was given the target's nil-raising slots.
|
|
1851
|
+
function applyEngineSlotRules(
|
|
1852
|
+
prepared: PreparedFunction,
|
|
1853
|
+
arms: readonly PreparedFunction[],
|
|
1854
|
+
structs: readonly ApiStruct[],
|
|
1855
|
+
bindingsRead: boolean,
|
|
1856
|
+
): PreparedFunction[] {
|
|
1857
|
+
const { name, parameters } = prepared.original;
|
|
1858
|
+
const prefix = `${name}:param:`;
|
|
1859
|
+
let ruled = [...arms];
|
|
1860
|
+
for (const [key, rule] of ENGINE_SLOT_RULES) {
|
|
1861
|
+
if (!key.startsWith(prefix)) continue;
|
|
1862
|
+
const slot = key.slice(prefix.length);
|
|
1863
|
+
const index = parameters.findIndex((p) => p.name === slot);
|
|
1864
|
+
if (index === -1) {
|
|
1865
|
+
throw new Error(`engine slot rule ${key}: ${name} has no ${slot} parameter`);
|
|
1866
|
+
}
|
|
1867
|
+
ruled = engineSlotRuleArms(prepared, ruled, key, rule, index, structs, bindingsRead);
|
|
1868
|
+
}
|
|
1869
|
+
return ruled;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// Every call a function declares, in order: its nil-raising arms, each split
|
|
1873
|
+
// at its presence-required fields, then narrowed by its engine slot rules.
|
|
1874
|
+
function callArms(
|
|
1875
|
+
prepared: PreparedFunction,
|
|
1876
|
+
nilRaisingSlots: ReadonlyMap<string, readonly number[]> | undefined,
|
|
1877
|
+
structs: readonly ApiStruct[],
|
|
1878
|
+
): PreparedFunction[] {
|
|
1879
|
+
const arms = nilRaisingArms(prepared, nilRaisingSlots ?? new Map()).flatMap((arm) =>
|
|
1880
|
+
presenceArms(arm, structs),
|
|
1881
|
+
);
|
|
1882
|
+
return applyEngineSlotRules(prepared, arms, structs, nilRaisingSlots !== undefined);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1336
1885
|
export type ConstantSlotResolution =
|
|
1337
1886
|
| { readonly borrow: string }
|
|
1338
1887
|
| { readonly family: string; readonly evidence: string }
|
|
@@ -1347,8 +1896,6 @@ export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResoluti
|
|
|
1347
1896
|
string,
|
|
1348
1897
|
ConstantSlotResolution
|
|
1349
1898
|
>([
|
|
1350
|
-
["gui.get:param:property", { borrow: "gui.animate:param:property" }],
|
|
1351
|
-
["gui.set:param:property", { borrow: "gui.animate:param:property" }],
|
|
1352
1899
|
["gui.cancel_animations:param:property", { borrow: "gui.animate:param:property" }],
|
|
1353
1900
|
[
|
|
1354
1901
|
"buffer.set_metadata:param:value_type",
|
|
@@ -1366,7 +1913,7 @@ export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResoluti
|
|
|
1366
1913
|
],
|
|
1367
1914
|
]);
|
|
1368
1915
|
|
|
1369
|
-
export interface
|
|
1916
|
+
export interface ConstantUnionNamedRow {
|
|
1370
1917
|
// The namespace the alias is declared in. It is the namespace the union
|
|
1371
1918
|
// *means*, which is not always the one its constants live in: the three
|
|
1372
1919
|
// buffer keys `render.clear` accepts are `graphics.BUFFER_TYPE_*` constants,
|
|
@@ -1377,11 +1924,22 @@ export interface ConstantUnionAlias {
|
|
|
1377
1924
|
readonly members: readonly string[];
|
|
1378
1925
|
}
|
|
1379
1926
|
|
|
1380
|
-
// A
|
|
1381
|
-
//
|
|
1382
|
-
//
|
|
1383
|
-
|
|
1384
|
-
|
|
1927
|
+
// A union that is a strict subset of a named row in the same home. It declares
|
|
1928
|
+
// nothing: each slot spells it `Exclude<<home>.<parent>, ...>`, so one namespace
|
|
1929
|
+
// never carries two names for overlapping sets.
|
|
1930
|
+
export interface ConstantUnionSubsetRow {
|
|
1931
|
+
readonly home: string;
|
|
1932
|
+
readonly parent: string;
|
|
1933
|
+
readonly members: readonly string[];
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
export type ConstantUnionAlias = ConstantUnionNamedRow | ConstantUnionSubsetRow;
|
|
1937
|
+
|
|
1938
|
+
// A documented constant union gets a public spelling so a caller can write the
|
|
1939
|
+
// type their own wrapper takes. Identity is the *member set*, never a name
|
|
1940
|
+
// prefix: the x and y anchor slots accept different thirds of `gui.ANCHOR_*`,
|
|
1941
|
+
// so each is its own subset row of `gui.ANCHOR`, and `graphics.BUFFER_TYPE_*`
|
|
1942
|
+
// splits because `render.clear` accepts three of the six.
|
|
1385
1943
|
//
|
|
1386
1944
|
// The table is the only thing the emitter reads, and every name in it has been
|
|
1387
1945
|
// reviewed: a longest-common-prefix rule proposes, a human disposes. A union
|
|
@@ -1391,7 +1949,7 @@ export interface ConstantUnionAlias {
|
|
|
1391
1949
|
export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
1392
1950
|
{
|
|
1393
1951
|
home: "buffer",
|
|
1394
|
-
name: "
|
|
1952
|
+
name: "VALUE_TYPE",
|
|
1395
1953
|
members: [
|
|
1396
1954
|
"buffer.VALUE_TYPE_FLOAT32",
|
|
1397
1955
|
"buffer.VALUE_TYPE_INT16",
|
|
@@ -1406,7 +1964,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1406
1964
|
},
|
|
1407
1965
|
{
|
|
1408
1966
|
home: "collectionfactory",
|
|
1409
|
-
name: "
|
|
1967
|
+
name: "STATUS",
|
|
1410
1968
|
members: [
|
|
1411
1969
|
"collectionfactory.STATUS_UNLOADED",
|
|
1412
1970
|
"collectionfactory.STATUS_LOADING",
|
|
@@ -1415,12 +1973,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1415
1973
|
},
|
|
1416
1974
|
{
|
|
1417
1975
|
home: "factory",
|
|
1418
|
-
name: "
|
|
1976
|
+
name: "STATUS",
|
|
1419
1977
|
members: ["factory.STATUS_UNLOADED", "factory.STATUS_LOADING", "factory.STATUS_LOADED"],
|
|
1420
1978
|
},
|
|
1421
1979
|
{
|
|
1422
1980
|
home: "go",
|
|
1423
|
-
name: "
|
|
1981
|
+
name: "EASING",
|
|
1424
1982
|
members: [
|
|
1425
1983
|
"go.EASING_INBACK",
|
|
1426
1984
|
"go.EASING_INBOUNCE",
|
|
@@ -1467,7 +2025,20 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1467
2025
|
},
|
|
1468
2026
|
{
|
|
1469
2027
|
home: "go",
|
|
1470
|
-
name: "
|
|
2028
|
+
name: "PLAYBACK",
|
|
2029
|
+
members: [
|
|
2030
|
+
"go.PLAYBACK_LOOP_BACKWARD",
|
|
2031
|
+
"go.PLAYBACK_LOOP_FORWARD",
|
|
2032
|
+
"go.PLAYBACK_LOOP_PINGPONG",
|
|
2033
|
+
"go.PLAYBACK_NONE",
|
|
2034
|
+
"go.PLAYBACK_ONCE_BACKWARD",
|
|
2035
|
+
"go.PLAYBACK_ONCE_FORWARD",
|
|
2036
|
+
"go.PLAYBACK_ONCE_PINGPONG",
|
|
2037
|
+
],
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
home: "go",
|
|
2041
|
+
parent: "PLAYBACK",
|
|
1471
2042
|
members: [
|
|
1472
2043
|
"go.PLAYBACK_ONCE_FORWARD",
|
|
1473
2044
|
"go.PLAYBACK_ONCE_BACKWARD",
|
|
@@ -1477,9 +2048,32 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1477
2048
|
"go.PLAYBACK_LOOP_PINGPONG",
|
|
1478
2049
|
],
|
|
1479
2050
|
},
|
|
2051
|
+
// The factors render_script.cpp:CheckBlendFactor accepts, in its order. Every
|
|
2052
|
+
// target defines the constants; only the newer ones name the union.
|
|
2053
|
+
{
|
|
2054
|
+
home: "graphics",
|
|
2055
|
+
name: "BLEND_FACTOR",
|
|
2056
|
+
members: [
|
|
2057
|
+
"graphics.BLEND_FACTOR_ZERO",
|
|
2058
|
+
"graphics.BLEND_FACTOR_ONE",
|
|
2059
|
+
"graphics.BLEND_FACTOR_SRC_COLOR",
|
|
2060
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_SRC_COLOR",
|
|
2061
|
+
"graphics.BLEND_FACTOR_DST_COLOR",
|
|
2062
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_DST_COLOR",
|
|
2063
|
+
"graphics.BLEND_FACTOR_SRC_ALPHA",
|
|
2064
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA",
|
|
2065
|
+
"graphics.BLEND_FACTOR_DST_ALPHA",
|
|
2066
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_DST_ALPHA",
|
|
2067
|
+
"graphics.BLEND_FACTOR_SRC_ALPHA_SATURATE",
|
|
2068
|
+
"graphics.BLEND_FACTOR_CONSTANT_COLOR",
|
|
2069
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR",
|
|
2070
|
+
"graphics.BLEND_FACTOR_CONSTANT_ALPHA",
|
|
2071
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA",
|
|
2072
|
+
],
|
|
2073
|
+
},
|
|
1480
2074
|
{
|
|
1481
2075
|
home: "graphics",
|
|
1482
|
-
name: "
|
|
2076
|
+
name: "BUFFER_TYPE",
|
|
1483
2077
|
members: [
|
|
1484
2078
|
"graphics.BUFFER_TYPE_COLOR0_BIT",
|
|
1485
2079
|
"graphics.BUFFER_TYPE_COLOR1_BIT",
|
|
@@ -1491,7 +2085,22 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1491
2085
|
},
|
|
1492
2086
|
{
|
|
1493
2087
|
home: "graphics",
|
|
1494
|
-
name: "
|
|
2088
|
+
name: "STATE",
|
|
2089
|
+
members: [
|
|
2090
|
+
"graphics.STATE_ALPHA_TEST",
|
|
2091
|
+
"graphics.STATE_ALPHA_TEST_SUPPORTED",
|
|
2092
|
+
"graphics.STATE_BLEND",
|
|
2093
|
+
"graphics.STATE_CULL_FACE",
|
|
2094
|
+
"graphics.STATE_DEPTH_TEST",
|
|
2095
|
+
"graphics.STATE_POLYGON_OFFSET_FILL",
|
|
2096
|
+
"graphics.STATE_SCISSOR_TEST",
|
|
2097
|
+
"graphics.STATE_STENCIL_TEST",
|
|
2098
|
+
],
|
|
2099
|
+
},
|
|
2100
|
+
// The six render_script.cpp:RenderScript_EnableState accepts.
|
|
2101
|
+
{
|
|
2102
|
+
home: "graphics",
|
|
2103
|
+
parent: "STATE",
|
|
1495
2104
|
members: [
|
|
1496
2105
|
"graphics.STATE_DEPTH_TEST",
|
|
1497
2106
|
"graphics.STATE_STENCIL_TEST",
|
|
@@ -1503,12 +2112,33 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1503
2112
|
},
|
|
1504
2113
|
{
|
|
1505
2114
|
home: "gui",
|
|
1506
|
-
name: "
|
|
2115
|
+
name: "ADJUST",
|
|
1507
2116
|
members: ["gui.ADJUST_FIT", "gui.ADJUST_ZOOM", "gui.ADJUST_STRETCH"],
|
|
1508
2117
|
},
|
|
1509
2118
|
{
|
|
1510
2119
|
home: "gui",
|
|
1511
|
-
name: "
|
|
2120
|
+
name: "ANCHOR",
|
|
2121
|
+
members: [
|
|
2122
|
+
"gui.ANCHOR_BOTTOM",
|
|
2123
|
+
"gui.ANCHOR_LEFT",
|
|
2124
|
+
"gui.ANCHOR_NONE",
|
|
2125
|
+
"gui.ANCHOR_RIGHT",
|
|
2126
|
+
"gui.ANCHOR_TOP",
|
|
2127
|
+
],
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
home: "gui",
|
|
2131
|
+
parent: "ANCHOR",
|
|
2132
|
+
members: ["gui.ANCHOR_NONE", "gui.ANCHOR_LEFT", "gui.ANCHOR_RIGHT"],
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
home: "gui",
|
|
2136
|
+
parent: "ANCHOR",
|
|
2137
|
+
members: ["gui.ANCHOR_NONE", "gui.ANCHOR_TOP", "gui.ANCHOR_BOTTOM"],
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
home: "gui",
|
|
2141
|
+
name: "BLEND",
|
|
1512
2142
|
members: [
|
|
1513
2143
|
"gui.BLEND_ALPHA",
|
|
1514
2144
|
"gui.BLEND_ADD",
|
|
@@ -1519,12 +2149,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1519
2149
|
},
|
|
1520
2150
|
{
|
|
1521
2151
|
home: "gui",
|
|
1522
|
-
name: "
|
|
2152
|
+
name: "CLIPPING_MODE",
|
|
1523
2153
|
members: ["gui.CLIPPING_MODE_NONE", "gui.CLIPPING_MODE_STENCIL"],
|
|
1524
2154
|
},
|
|
1525
2155
|
{
|
|
1526
2156
|
home: "gui",
|
|
1527
|
-
name: "
|
|
2157
|
+
name: "EASING",
|
|
1528
2158
|
members: [
|
|
1529
2159
|
"gui.EASING_INBACK",
|
|
1530
2160
|
"gui.EASING_INBOUNCE",
|
|
@@ -1571,7 +2201,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1571
2201
|
},
|
|
1572
2202
|
{
|
|
1573
2203
|
home: "gui",
|
|
1574
|
-
name: "
|
|
2204
|
+
name: "KEYBOARD_TYPE",
|
|
1575
2205
|
members: [
|
|
1576
2206
|
"gui.KEYBOARD_TYPE_DEFAULT",
|
|
1577
2207
|
"gui.KEYBOARD_TYPE_EMAIL",
|
|
@@ -1581,7 +2211,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1581
2211
|
},
|
|
1582
2212
|
{
|
|
1583
2213
|
home: "gui",
|
|
1584
|
-
name: "
|
|
2214
|
+
name: "TYPE",
|
|
1585
2215
|
members: [
|
|
1586
2216
|
"gui.TYPE_BOX",
|
|
1587
2217
|
"gui.TYPE_TEXT",
|
|
@@ -1592,12 +2222,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1592
2222
|
},
|
|
1593
2223
|
{
|
|
1594
2224
|
home: "gui",
|
|
1595
|
-
name: "
|
|
2225
|
+
name: "PIEBOUNDS",
|
|
1596
2226
|
members: ["gui.PIEBOUNDS_RECTANGLE", "gui.PIEBOUNDS_ELLIPSE"],
|
|
1597
2227
|
},
|
|
1598
2228
|
{
|
|
1599
2229
|
home: "gui",
|
|
1600
|
-
name: "
|
|
2230
|
+
name: "PIVOT",
|
|
1601
2231
|
members: [
|
|
1602
2232
|
"gui.PIVOT_CENTER",
|
|
1603
2233
|
"gui.PIVOT_N",
|
|
@@ -1612,7 +2242,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1612
2242
|
},
|
|
1613
2243
|
{
|
|
1614
2244
|
home: "gui",
|
|
1615
|
-
name: "
|
|
2245
|
+
name: "PLAYBACK",
|
|
1616
2246
|
members: [
|
|
1617
2247
|
"gui.PLAYBACK_ONCE_FORWARD",
|
|
1618
2248
|
"gui.PLAYBACK_ONCE_BACKWARD",
|
|
@@ -1624,7 +2254,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1624
2254
|
},
|
|
1625
2255
|
{
|
|
1626
2256
|
home: "gui",
|
|
1627
|
-
name: "
|
|
2257
|
+
name: "PROP",
|
|
1628
2258
|
members: [
|
|
1629
2259
|
"gui.PROP_POSITION",
|
|
1630
2260
|
"gui.PROP_ROTATION",
|
|
@@ -1643,7 +2273,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1643
2273
|
},
|
|
1644
2274
|
{
|
|
1645
2275
|
home: "gui",
|
|
1646
|
-
name: "
|
|
2276
|
+
name: "SAFE_AREA",
|
|
1647
2277
|
members: [
|
|
1648
2278
|
"gui.SAFE_AREA_NONE",
|
|
1649
2279
|
"gui.SAFE_AREA_LONG",
|
|
@@ -1653,22 +2283,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1653
2283
|
},
|
|
1654
2284
|
{
|
|
1655
2285
|
home: "gui",
|
|
1656
|
-
name: "
|
|
2286
|
+
name: "SIZE_MODE",
|
|
1657
2287
|
members: ["gui.SIZE_MODE_MANUAL", "gui.SIZE_MODE_AUTO"],
|
|
1658
2288
|
},
|
|
1659
|
-
{
|
|
1660
|
-
home: "gui",
|
|
1661
|
-
name: "XAnchor",
|
|
1662
|
-
members: ["gui.ANCHOR_NONE", "gui.ANCHOR_LEFT", "gui.ANCHOR_RIGHT"],
|
|
1663
|
-
},
|
|
1664
|
-
{
|
|
1665
|
-
home: "gui",
|
|
1666
|
-
name: "YAnchor",
|
|
1667
|
-
members: ["gui.ANCHOR_NONE", "gui.ANCHOR_TOP", "gui.ANCHOR_BOTTOM"],
|
|
1668
|
-
},
|
|
1669
2289
|
{
|
|
1670
2290
|
home: "profiler",
|
|
1671
|
-
name: "
|
|
2291
|
+
name: "MODE",
|
|
1672
2292
|
members: [
|
|
1673
2293
|
"profiler.MODE_RUN",
|
|
1674
2294
|
"profiler.MODE_PAUSE",
|
|
@@ -1678,7 +2298,7 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1678
2298
|
},
|
|
1679
2299
|
{
|
|
1680
2300
|
home: "profiler",
|
|
1681
|
-
name: "
|
|
2301
|
+
name: "VIEW_MODE",
|
|
1682
2302
|
members: ["profiler.VIEW_MODE_FULL", "profiler.VIEW_MODE_MINIMIZED"],
|
|
1683
2303
|
},
|
|
1684
2304
|
{
|
|
@@ -1690,9 +2310,15 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1690
2310
|
"graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
1691
2311
|
],
|
|
1692
2312
|
},
|
|
2313
|
+
// The render-target creator raises on any other texture type.
|
|
2314
|
+
{
|
|
2315
|
+
home: "render",
|
|
2316
|
+
name: "RenderTargetTextureType",
|
|
2317
|
+
members: ["graphics.TEXTURE_TYPE_2D", "graphics.TEXTURE_TYPE_CUBE_MAP"],
|
|
2318
|
+
},
|
|
1693
2319
|
{
|
|
1694
2320
|
home: "sys",
|
|
1695
|
-
name: "
|
|
2321
|
+
name: "NETWORK",
|
|
1696
2322
|
members: [
|
|
1697
2323
|
"sys.NETWORK_DISCONNECTED",
|
|
1698
2324
|
"sys.NETWORK_CONNECTED_CELLULAR",
|
|
@@ -1701,12 +2327,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1701
2327
|
},
|
|
1702
2328
|
{
|
|
1703
2329
|
home: "window",
|
|
1704
|
-
name: "
|
|
2330
|
+
name: "DIMMING",
|
|
1705
2331
|
members: ["window.DIMMING_UNKNOWN", "window.DIMMING_ON", "window.DIMMING_OFF"],
|
|
1706
2332
|
},
|
|
1707
2333
|
{
|
|
1708
2334
|
home: "window",
|
|
1709
|
-
|
|
2335
|
+
parent: "DIMMING",
|
|
1710
2336
|
members: ["window.DIMMING_ON", "window.DIMMING_OFF"],
|
|
1711
2337
|
},
|
|
1712
2338
|
];
|
|
@@ -1726,11 +2352,45 @@ export function constantUnionAlias(fqns: readonly string[]): ConstantUnionAlias
|
|
|
1726
2352
|
return CONSTANT_UNION_ALIAS_BY_MEMBERS.get(constantUnionAliasKey(fqns));
|
|
1727
2353
|
}
|
|
1728
2354
|
|
|
1729
|
-
//
|
|
1730
|
-
//
|
|
1731
|
-
//
|
|
1732
|
-
export function
|
|
1733
|
-
|
|
2355
|
+
// What a slot taking exactly the row's members is written as: a named row's
|
|
2356
|
+
// qualified name, or a subset row's parent minus the parent's other members,
|
|
2357
|
+
// listed in the parent's order.
|
|
2358
|
+
export function constantUnionSpelling(row: ConstantUnionAlias): string {
|
|
2359
|
+
if ("name" in row) return `${row.home}.${engineTypeName(row.name)}`;
|
|
2360
|
+
const parent = CONSTANT_UNION_ALIASES.find(
|
|
2361
|
+
(candidate): candidate is ConstantUnionNamedRow =>
|
|
2362
|
+
"name" in candidate && candidate.home === row.home && candidate.name === row.parent,
|
|
2363
|
+
);
|
|
2364
|
+
if (parent === undefined) {
|
|
2365
|
+
throw new Error(`constant union subset of ${row.home}.${row.parent}: no such named row`);
|
|
2366
|
+
}
|
|
2367
|
+
const kept = new Set(row.members);
|
|
2368
|
+
const excluded = parent.members.filter((member) => !kept.has(member));
|
|
2369
|
+
return `Exclude<${row.home}.${engineTypeName(row.parent)}, ${excluded.map((m) => `typeof ${m}`).join(" | ")}>`;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// The named rows a module owns; a subset row declares nothing. A row is
|
|
2373
|
+
// declared in its home whether or not that module references it, so a
|
|
2374
|
+
// cross-namespace row is reachable from the module whose signatures spell it.
|
|
2375
|
+
// A row whose name the module's own `ENUM` already declares is left to that
|
|
2376
|
+
// `ENUM`, which carries the reference's member order and nilable arms.
|
|
2377
|
+
function ownedConstantUnionAliases(
|
|
2378
|
+
namespace: string,
|
|
2379
|
+
enumDeclared: ReadonlySet<string>,
|
|
2380
|
+
): ConstantUnionNamedRow[] {
|
|
2381
|
+
return CONSTANT_UNION_ALIASES.filter(
|
|
2382
|
+
(row): row is ConstantUnionNamedRow =>
|
|
2383
|
+
"name" in row && row.home === namespace && !enumDeclared.has(row.name),
|
|
2384
|
+
);
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
// The alias declarations a module owns, in name order.
|
|
2388
|
+
export function constantUnionAliasDeclarations(
|
|
2389
|
+
namespace: string,
|
|
2390
|
+
enumDeclared: ReadonlySet<string> = new Set(),
|
|
2391
|
+
): string[] {
|
|
2392
|
+
return ownedConstantUnionAliases(namespace, enumDeclared)
|
|
2393
|
+
.map((row) => ({ name: engineTypeName(row.name), members: row.members }))
|
|
1734
2394
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
1735
2395
|
.map((row) => unionAliasDeclaration(row.name, row.members));
|
|
1736
2396
|
}
|
|
@@ -1746,10 +2406,12 @@ function unionAliasDeclaration(
|
|
|
1746
2406
|
}
|
|
1747
2407
|
|
|
1748
2408
|
// A slot of a function the authored-overload lane declares, published under
|
|
1749
|
-
// one
|
|
1750
|
-
//
|
|
2409
|
+
// one name on every target so the version-independent overload file can name
|
|
2410
|
+
// the type each target's own reference gives that slot. The name is the
|
|
2411
|
+
// PascalCase name of the newest reference's own declaration for the slot; a
|
|
2412
|
+
// target that declares it emits no alias.
|
|
1751
2413
|
export const OVERLOAD_SLOT_ALIASES: ReadonlyMap<string, string> = new Map([
|
|
1752
|
-
["render.render_target:param:parameters", "
|
|
2414
|
+
["render.render_target:param:parameters", engineTypeName("render_target_params")],
|
|
1753
2415
|
]);
|
|
1754
2416
|
|
|
1755
2417
|
const DOC_CONSTANT_TOKEN =
|
|
@@ -1801,9 +2463,14 @@ function constantSlotTokens(
|
|
|
1801
2463
|
return documentedConstantTokens(entry.family, universe);
|
|
1802
2464
|
}
|
|
1803
2465
|
|
|
1804
|
-
function
|
|
2466
|
+
// A borrow reads the ref-doc, so a slot of a function the skip rules withheld
|
|
2467
|
+
// (`gui.animate`, which gui-overloads.d.ts declares) still lends its doc.
|
|
2468
|
+
function slotDocLookup(
|
|
2469
|
+
module: ApiModule,
|
|
2470
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
2471
|
+
): SlotDocLookup {
|
|
1805
2472
|
const docs = new Map<string, string>();
|
|
1806
|
-
for (const fn of module.functions) {
|
|
2473
|
+
for (const fn of [...module.functions, ...withheldFunctions]) {
|
|
1807
2474
|
for (const p of fn.parameters) {
|
|
1808
2475
|
const key = tableSlotKey(fn.name, "param", p.name);
|
|
1809
2476
|
if (!docs.has(key)) docs.set(key, p.doc);
|
|
@@ -1823,8 +2490,9 @@ export function resolveConstantSlotTokens(
|
|
|
1823
2490
|
module: ApiModule,
|
|
1824
2491
|
slotKey: string,
|
|
1825
2492
|
universe: ReadonlySet<string>,
|
|
2493
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
1826
2494
|
): string[] {
|
|
1827
|
-
const lookupDoc = slotDocLookup(module);
|
|
2495
|
+
const lookupDoc = slotDocLookup(module, withheldFunctions);
|
|
1828
2496
|
return constantSlotTokens(lookupDoc(slotKey) ?? "", slotKey, lookupDoc, universe);
|
|
1829
2497
|
}
|
|
1830
2498
|
|
|
@@ -1838,21 +2506,23 @@ type ConstantSlotTokens = (
|
|
|
1838
2506
|
function constantSlotTokenResolver(
|
|
1839
2507
|
module: ApiModule,
|
|
1840
2508
|
universe: ReadonlySet<string>,
|
|
2509
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
1841
2510
|
): ConstantSlotTokens {
|
|
1842
|
-
const lookupDoc = slotDocLookup(module);
|
|
2511
|
+
const lookupDoc = slotDocLookup(module, withheldFunctions);
|
|
1843
2512
|
return (elementName, slotKind, slotName, doc) =>
|
|
1844
2513
|
constantSlotTokens(doc, tableSlotKey(elementName, slotKind, slotName), lookupDoc, universe);
|
|
1845
2514
|
}
|
|
1846
2515
|
|
|
1847
|
-
// FQN-keyed allowlist of the
|
|
1848
|
-
//
|
|
1849
|
-
//
|
|
1850
|
-
//
|
|
1851
|
-
//
|
|
1852
|
-
// `
|
|
1853
|
-
//
|
|
1854
|
-
//
|
|
1855
|
-
//
|
|
2516
|
+
// FQN-keyed allowlist of the checks that genuinely narrow their argument, mapped
|
|
2517
|
+
// to the `DEFOLD_TYPE_MAP` token whose type they prove: the `types.is_*` checks,
|
|
2518
|
+
// and `bullet3d.collision_object.is_rigid_body`, whose binding is a
|
|
2519
|
+
// `btRigidBody::upcast` test. Emitting these as user-defined type guards
|
|
2520
|
+
// (`var_ is Vector3`) is the only way to narrow an engine userdata value, which
|
|
2521
|
+
// `typeof` cannot. An allowlist, not a blanket `is_*` match: `b2d.body.is_active`,
|
|
2522
|
+
// `gui.is_enabled`, `sound.is_music_playing`, `go.exists`, `sys.exists` are
|
|
2523
|
+
// runtime-state / existence checks on already-typed arguments and must stay
|
|
2524
|
+
// `boolean`. An unlisted future `types.is_*` also stays a visible `boolean` until
|
|
2525
|
+
// curated here, matching the ARBITRARY_TABLE_SLOTS deliberate-opt-in precedent.
|
|
1856
2526
|
export const TYPE_PREDICATES: ReadonlyMap<string, string> = new Map([
|
|
1857
2527
|
["types.is_hash", "hash"],
|
|
1858
2528
|
["types.is_matrix4", "matrix4"],
|
|
@@ -1861,13 +2531,47 @@ export const TYPE_PREDICATES: ReadonlyMap<string, string> = new Map([
|
|
|
1861
2531
|
["types.is_vector", "vector"],
|
|
1862
2532
|
["types.is_vector3", "vector3"],
|
|
1863
2533
|
["types.is_vector4", "vector4"],
|
|
2534
|
+
["bullet3d.collision_object.is_rigid_body", "btRigidBody"],
|
|
2535
|
+
]);
|
|
2536
|
+
|
|
2537
|
+
// The merged ref-doc gives each Box2D backend's form of these queries one
|
|
2538
|
+
// parameter list, so the first (v2) arm always wins and a v3 hit is unreachable.
|
|
2539
|
+
// A type parameter tells them apart: v2/script_box2d_world_v2.cpp:PushCastHit
|
|
2540
|
+
// sets `fixture` and `shape` to one PushFixtureInfo table, while
|
|
2541
|
+
// v3/script_box2d_world_v3.cpp:PushCastHit sets `shape` alone from
|
|
2542
|
+
// PushShapeInfoForShape. The v2 arm is declared first, so a call with no type
|
|
2543
|
+
// argument keeps the v2 hits; its `= "v2"` default only states that. Keyed by
|
|
2544
|
+
// `box2dBackendArmKey`.
|
|
2545
|
+
export const BOX2D_BACKEND_ARMS: ReadonlyMap<string, Box2dBackend> = new Map<string, Box2dBackend>([
|
|
2546
|
+
["b2d.world.cast_ray:b2d.fixture_cast_hit[]", "v2"],
|
|
2547
|
+
["b2d.world.cast_ray:b2d.shape_cast_hit[]", "v3"],
|
|
2548
|
+
["b2d.world.cast_ray_closest:b2d.fixture_cast_hit", "v2"],
|
|
2549
|
+
["b2d.world.cast_ray_closest:b2d.shape_cast_hit", "v3"],
|
|
2550
|
+
["b2d.world.cast_shape:b2d.fixture_cast_hit[]", "v2"],
|
|
2551
|
+
["b2d.world.cast_shape:b2d.shape_cast_hit[]", "v3"],
|
|
2552
|
+
["b2d.world.overlap_aabb:b2d.fixture_info[]", "v2"],
|
|
2553
|
+
["b2d.world.overlap_aabb:b2d.shape_info[]", "v3"],
|
|
2554
|
+
["b2d.world.overlap_shape:b2d.fixture_info[]", "v2"],
|
|
2555
|
+
["b2d.world.overlap_shape:b2d.shape_info[]", "v3"],
|
|
1864
2556
|
]);
|
|
1865
2557
|
|
|
2558
|
+
// `<fqn>:<first non-nil type of the first return>`: the two arms of each
|
|
2559
|
+
// `BOX2D_BACKEND_ARMS` query share every parameter and differ only there.
|
|
2560
|
+
export function box2dBackendArmKey(fn: ApiFunction): string {
|
|
2561
|
+
const first = fn.returnValues[0]?.types.find((type) => type !== "nil") ?? "";
|
|
2562
|
+
return `${fn.name}:${first}`;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
function box2dBackendTypeParameters(fn: ApiFunction): string {
|
|
2566
|
+
const backend = BOX2D_BACKEND_ARMS.get(box2dBackendArmKey(fn));
|
|
2567
|
+
if (backend === undefined) return "";
|
|
2568
|
+
return backend === "v2" ? '<Backend extends "v2" = "v2">' : '<Backend extends "v3">';
|
|
2569
|
+
}
|
|
2570
|
+
|
|
1866
2571
|
// Element names whose `table` slot is a prose-only `array/list/table of <T>` shape
|
|
1867
2572
|
// the field-list parser cannot read, but whose element type a human curated from
|
|
1868
2573
|
// the doc. The value is a single element token (`T[]`) or a token list when the
|
|
1869
|
-
// element is itself a union
|
|
1870
|
-
// `string | hash | url`, emitted `(string | Hash | Url)[]`. Emitted as a plain
|
|
2574
|
+
// element is itself a union, emitted `(A | B)[]`. Emitted as a plain
|
|
1871
2575
|
// array, never `LuaMap`: an array element carries no illegal-index-key problem so
|
|
1872
2576
|
// no TSTL wiring is needed. The audit consults this map too, so the gate and the
|
|
1873
2577
|
// emitted surface stay coupled; a slot not curated here still surfaces under
|
|
@@ -1882,7 +2586,6 @@ export const HOMOGENEOUS_ARRAY_SLOTS: ReadonlyMap<string, string | readonly stri
|
|
|
1882
2586
|
["vmath.vector", "number"],
|
|
1883
2587
|
["sound.get_groups", "hash"],
|
|
1884
2588
|
["iap.list", "string"],
|
|
1885
|
-
["go.delete", ["string", "hash", "url"]],
|
|
1886
2589
|
// push.register's `notifications` is a prose-only array of push.NOTIFICATION_*
|
|
1887
2590
|
// bitmask constants (the ref example sums NOTIFICATION_BADGE/SOUND/ALERT). The
|
|
1888
2591
|
// vendored push_doc.json fixture declares no NOTIFICATION_* constant elements, so
|
|
@@ -1905,7 +2608,11 @@ export type NestedMapping = { key: string; value: string };
|
|
|
1905
2608
|
// for `collectionfactory.create`'s properties.
|
|
1906
2609
|
export type TableSlotCuration = (
|
|
1907
2610
|
| { kind: "mapping"; key: string; value: string | readonly TableField[] | NestedMapping }
|
|
1908
|
-
|
|
2611
|
+
// `pairs` also admits any table whose values are the element, for a binding
|
|
2612
|
+
// that walks the table with lua_next and reads only its values. The extra arm
|
|
2613
|
+
// is `LuaPairsIterable`, which is covariant in its value; `LuaTable` is
|
|
2614
|
+
// invariant through `set`, so it would reject a `LuaTable<Hash, Hash>`.
|
|
2615
|
+
| { kind: "array"; element: string | readonly string[]; pairs?: true }
|
|
1909
2616
|
| { kind: "object"; fields: readonly TableField[] }
|
|
1910
2617
|
| { kind: "array-object"; fields: readonly TableField[] }
|
|
1911
2618
|
// A `<key> -> <args table>` slot: the doc's `<dl>` describes the *value* shape,
|
|
@@ -1918,7 +2625,34 @@ export type TableSlotCuration = (
|
|
|
1918
2625
|
| { kind: "verbatim"; ts: string }
|
|
1919
2626
|
) & { readonly retypedUpstream?: readonly string[] };
|
|
1920
2627
|
|
|
1921
|
-
|
|
2628
|
+
// The interface a module's component properties are declared on; its PROPERTY
|
|
2629
|
+
// entries carry its members, so it signs no TYPEDEF of its own.
|
|
2630
|
+
export const PROPERTIES_INTERFACE = engineTypeName("properties");
|
|
2631
|
+
|
|
2632
|
+
// The ref-doc tokens of the handles `socket.select` takes and returns, in their
|
|
2633
|
+
// 1.13.2 TYPEDEF spelling, which the type map resolves to each handle's interface.
|
|
2634
|
+
export const SOCKET_HANDLE_TOKENS = (["client", "master", "unconnected"] as const).map(
|
|
2635
|
+
(receiver) => `socket_${receiver}`,
|
|
2636
|
+
);
|
|
2637
|
+
|
|
2638
|
+
// The interface a module's `<receiver>:<method>` colon methods are emitted on:
|
|
2639
|
+
// the socket handles take their `Socket<Receiver>` name, any other receiver
|
|
2640
|
+
// keeps its own.
|
|
2641
|
+
export function handleInterfaceName(namespace: string, receiver: string): string {
|
|
2642
|
+
return namespace === "socket" && Object.hasOwn(SOCKET_HANDLE_NAMES, receiver)
|
|
2643
|
+
? SOCKET_HANDLE_NAMES[receiver as SocketHandleReceiver]
|
|
2644
|
+
: receiver;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
// The typedef names a module's handle interfaces stand for, which emit as the
|
|
2648
|
+
// interface rather than an alias: each receiver (1.13.1 `client`) and each
|
|
2649
|
+
// interface name, which the 1.13.2 `socket_client` takes once renamed
|
|
2650
|
+
// (`SocketClient`).
|
|
2651
|
+
function handleTypedefNames(namespace: string, receivers: Iterable<string>): Set<string> {
|
|
2652
|
+
return new Set(
|
|
2653
|
+
[...receivers].flatMap((receiver) => [receiver, handleInterfaceName(namespace, receiver)]),
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
1922
2656
|
|
|
1923
2657
|
// Shared Box2D (b2d.*) table shapes, curated from the ref-doc prose (the docs
|
|
1924
2658
|
// name fields in running text, not a machine-readable `<dl>`). Reused across the
|
|
@@ -2082,25 +2816,6 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2082
2816
|
string,
|
|
2083
2817
|
TableSlotCuration
|
|
2084
2818
|
>([
|
|
2085
|
-
// gui_script.cpp:LuaGet and LuaSet read the `key` and `keys` options. On a
|
|
2086
|
-
// prose `table` slot TABLE_SLOT_FIELD_ADDITIONS supplies them; 1.13.2 types the
|
|
2087
|
-
// slots as an inline record and a struct that omit them.
|
|
2088
|
-
...(
|
|
2089
|
-
[
|
|
2090
|
-
["gui.get:param:options", "{ index?:integer }"],
|
|
2091
|
-
["gui.set:param:options", "gui.set_options"],
|
|
2092
|
-
] as const
|
|
2093
|
-
).map(
|
|
2094
|
-
([key, retyped]) =>
|
|
2095
|
-
[
|
|
2096
|
-
key,
|
|
2097
|
-
{
|
|
2098
|
-
kind: "verbatim",
|
|
2099
|
-
ts: "{ index?: number; key?: string | Hash; keys?: (Hash | string)[] }",
|
|
2100
|
-
retypedUpstream: [retyped],
|
|
2101
|
-
},
|
|
2102
|
-
] as const,
|
|
2103
|
-
),
|
|
2104
2819
|
// buffer.create's `declaration` is "a table where each entry (table) describes
|
|
2105
2820
|
// a stream", and upstream's example passes a list of stream records whose
|
|
2106
2821
|
// `type` is a buffer.VALUE_TYPE_* constant.
|
|
@@ -2128,6 +2843,19 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2128
2843
|
},
|
|
2129
2844
|
],
|
|
2130
2845
|
["collectionfactory.create:return:ids", { kind: "mapping", key: "hash", value: "hash" }],
|
|
2846
|
+
// go.delete's "table of id's": gameobject_script.cpp:DeleteGOTable walks any
|
|
2847
|
+
// table with lua_next and resolves each value with ResolveURL, so the id table
|
|
2848
|
+
// collectionfactory.create returns deletes as it is. 1.13.2 retypes the slot
|
|
2849
|
+
// to an id array, which still rejects that table.
|
|
2850
|
+
[
|
|
2851
|
+
"go.delete:param:id",
|
|
2852
|
+
{
|
|
2853
|
+
kind: "array",
|
|
2854
|
+
element: ["string", "hash", "url"],
|
|
2855
|
+
pairs: true,
|
|
2856
|
+
retypedUpstream: ["(string|hash|url)[]"],
|
|
2857
|
+
},
|
|
2858
|
+
],
|
|
2131
2859
|
// font.get_info's `info` return is a `<dl>` with `path: hash` and a nested
|
|
2132
2860
|
// `fonts` field whose `<dd>` is itself a `<dl>` (`path: string`, `path_hash:
|
|
2133
2861
|
// hash`). parseTableFields flattens the inner `<dl>` to the top level, which
|
|
@@ -2375,70 +3103,14 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2375
3103
|
// depth/stencil. Both sides are union tokens the mapping string branch splits,
|
|
2376
3104
|
// so the key emits the three branded constants and the value `number | Vector4`.
|
|
2377
3105
|
// The key set is a CONSTANT_UNION_ALIASES row, so the signature carries the
|
|
2378
|
-
// `render.ClearBufferKey` alias rather than the three branded arms.
|
|
2379
|
-
[
|
|
2380
|
-
"render.clear:param:buffers",
|
|
2381
|
-
{
|
|
2382
|
-
retypedUpstream: ["table<graphics.BUFFER_TYPE, number|vector4>"],
|
|
2383
|
-
kind: "mapping",
|
|
2384
|
-
key: "graphics.BUFFER_TYPE_COLOR0_BIT | graphics.BUFFER_TYPE_DEPTH_BIT | graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
2385
|
-
value: "number | vector4",
|
|
2386
|
-
},
|
|
2387
|
-
],
|
|
2388
|
-
// go.on_input and gui.on_input share the same well-known InputAction shape
|
|
2389
|
-
// (the on_input function description lists the well-known fields: value,
|
|
2390
|
-
// pressed, released, repeated, x/y/screen_x/screen_y/dx/dy/screen_dx/screen_dy,
|
|
2391
|
-
// plus gamepad/touch/text multi-touch fields). The doc markup carries no
|
|
2392
|
-
// field list of its own, so the shape is curated verbatim from the prose.
|
|
2393
|
-
// Param-side optional fields: each input kind (mouse/key/text/gamepad) sets a
|
|
2394
|
-
// different subset, so the call site always sees an `?` on every field.
|
|
2395
|
-
// `touch` stays `table` (the nested multi-touch table has no machine-readable
|
|
2396
|
-
// field list; mirrors the render.set_render_target.transient recursive case).
|
|
2397
|
-
// The element is the bare `on_input` script hook (no namespace prefix), the
|
|
2398
|
-
// same form the `on_message` arbitrary-table entry uses. The doc carries a
|
|
2399
|
-
// well-formed touch input table, so `touch` is curated as a nested
|
|
2400
|
-
// array-of-touch-records (the field has its own `<table>` listing id,
|
|
2401
|
-
// pressed, released, tap_count, x, y, dx, dy, acc_x/y/z).
|
|
2402
|
-
[
|
|
2403
|
-
"on_input:param:action",
|
|
2404
|
-
{
|
|
2405
|
-
kind: "object",
|
|
2406
|
-
fields: [
|
|
2407
|
-
{ name: "value", types: ["number"], optional: true },
|
|
2408
|
-
{ name: "pressed", types: ["boolean"], optional: true },
|
|
2409
|
-
{ name: "released", types: ["boolean"], optional: true },
|
|
2410
|
-
{ name: "repeated", types: ["boolean"], optional: true },
|
|
2411
|
-
{ name: "x", types: ["number"], optional: true },
|
|
2412
|
-
{ name: "y", types: ["number"], optional: true },
|
|
2413
|
-
{ name: "screen_x", types: ["number"], optional: true },
|
|
2414
|
-
{ name: "screen_y", types: ["number"], optional: true },
|
|
2415
|
-
{ name: "dx", types: ["number"], optional: true },
|
|
2416
|
-
{ name: "dy", types: ["number"], optional: true },
|
|
2417
|
-
{ name: "screen_dx", types: ["number"], optional: true },
|
|
2418
|
-
{ name: "screen_dy", types: ["number"], optional: true },
|
|
2419
|
-
{ name: "gamepad", types: ["number"], optional: true },
|
|
2420
|
-
{ name: "gamepad_axis", types: ["vector3"], optional: true },
|
|
2421
|
-
{
|
|
2422
|
-
name: "touch",
|
|
2423
|
-
types: ["table"],
|
|
2424
|
-
optional: true,
|
|
2425
|
-
isList: true,
|
|
2426
|
-
fields: [
|
|
2427
|
-
{ name: "id", types: ["number"] },
|
|
2428
|
-
{ name: "pressed", types: ["boolean"] },
|
|
2429
|
-
{ name: "released", types: ["boolean"] },
|
|
2430
|
-
{ name: "tap_count", types: ["number"] },
|
|
2431
|
-
{ name: "x", types: ["number"] },
|
|
2432
|
-
{ name: "y", types: ["number"] },
|
|
2433
|
-
{ name: "dx", types: ["number"] },
|
|
2434
|
-
{ name: "dy", types: ["number"] },
|
|
2435
|
-
{ name: "acc_x", types: ["number"] },
|
|
2436
|
-
{ name: "acc_y", types: ["number"] },
|
|
2437
|
-
{ name: "acc_z", types: ["number"] },
|
|
2438
|
-
],
|
|
2439
|
-
},
|
|
2440
|
-
{ name: "text", types: ["string"], optional: true },
|
|
2441
|
-
],
|
|
3106
|
+
// `render.ClearBufferKey` alias rather than the three branded arms.
|
|
3107
|
+
[
|
|
3108
|
+
"render.clear:param:buffers",
|
|
3109
|
+
{
|
|
3110
|
+
retypedUpstream: ["table<graphics.BUFFER_TYPE, number|vector4>"],
|
|
3111
|
+
kind: "mapping",
|
|
3112
|
+
key: "graphics.BUFFER_TYPE_COLOR0_BIT | graphics.BUFFER_TYPE_DEPTH_BIT | graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
3113
|
+
value: "number | vector4",
|
|
2442
3114
|
},
|
|
2443
3115
|
],
|
|
2444
3116
|
// physics.create_joint and physics.set_joint_properties both declare
|
|
@@ -2932,7 +3604,9 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2932
3604
|
// no `status`; typing it as its own shape needs a per-target authored overload,
|
|
2933
3605
|
// so `status` stays required and the slot's `note` says how to tell the two
|
|
2934
3606
|
// apart. Its typed form is the same signature: the typed reference names
|
|
2935
|
-
// `http.response`, which the runtime surface withholds.
|
|
3607
|
+
// `http.response`, which the runtime surface withholds. Both forms add the
|
|
3608
|
+
// `sender` no reference lists: comp_script.cpp delivers the response as a
|
|
3609
|
+
// message, with `PushURL(L, message->m_Sender)` then `PCall(L, 4, 0)`.
|
|
2936
3610
|
// `model.play_anim`'s completion callback receives self, the message id and the
|
|
2937
3611
|
// message only: script_model.cpp calls it with `PCall(L, 3, 0)`, so both forms
|
|
2938
3612
|
// drop the `sender` every reference lists and type the message as the typed
|
|
@@ -2973,13 +3647,13 @@ export const CALLBACK_SIGNATURE_CURATIONS: ReadonlyMap<string, CallbackSignature
|
|
|
2973
3647
|
"(self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: Record<string | number, unknown>) => void",
|
|
2974
3648
|
typed:
|
|
2975
3649
|
// biome-ignore lint/suspicious/noTemplateCurlyInString: emitted verbatim as a TS template literal type
|
|
2976
|
-
"(self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: window.
|
|
3650
|
+
"(self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: window.EventData) => void",
|
|
2977
3651
|
},
|
|
2978
3652
|
],
|
|
2979
3653
|
[
|
|
2980
3654
|
"http.request:param:callback",
|
|
2981
3655
|
onBothForms(
|
|
2982
|
-
"(self: unknown, id: Hash, response: { status: number; url: string; response?: string; headers?: LuaMap<string, string>; path?: string; error?: string; bytes_sent?: number; bytes_received?: number; bytes_total?: number; range_start?: number; range_end?: number; document_size?: number }) => void",
|
|
3656
|
+
"(self: unknown, id: Hash, response: { status: number; url: string; response?: string; headers?: LuaMap<string, string>; path?: string; error?: string; bytes_sent?: number; bytes_received?: number; bytes_total?: number; range_start?: number; range_end?: number; document_size?: number }, sender: Url) => void",
|
|
2983
3657
|
"With `report_progress` set, the callback also receives progress events carrying `bytes_sent`, `bytes_received`, `bytes_total` and `url` but no `status`; `response.bytes_received !== undefined` marks a progress event, since a final response never carries it.",
|
|
2984
3658
|
),
|
|
2985
3659
|
],
|
|
@@ -3041,9 +3715,11 @@ export const NESTED_FIELD_CURATIONS: ReadonlyMap<string, readonly TableField[]>
|
|
|
3041
3715
|
// `<element>:<kind>:<slot>:<field>`, mirroring NESTED_FIELD_CURATIONS; a field
|
|
3042
3716
|
// one level down is `<field>.<member>`, or `<field>[].<member>` inside a list.
|
|
3043
3717
|
//
|
|
3044
|
-
//
|
|
3045
|
-
//
|
|
3046
|
-
// the bare token cannot
|
|
3718
|
+
// Unpinned records below rest on ref-doc tokens or prose. The atlas animation
|
|
3719
|
+
// `playback` field is documented as `constant` with the prose "the default value
|
|
3720
|
+
// is go.PLAYBACK_ONCE_FORWARD", which names the enum the bare token cannot; its
|
|
3721
|
+
// records also carry engine binding pins and apply only when the matching
|
|
3722
|
+
// `EmitOptions.bindingEvidence` key holds for the target.
|
|
3047
3723
|
//
|
|
3048
3724
|
// `gui.set`'s `options.key` is documented as `hash`, while upstream's own
|
|
3049
3725
|
// example sets a named font with `{ key = "my_font_name" }`.
|
|
@@ -3056,21 +3732,47 @@ export const NESTED_FIELD_CURATIONS: ReadonlyMap<string, readonly TableField[]>
|
|
|
3056
3732
|
// script_resource.cpp:CreateSoundData reads it with CheckBufferOrString.
|
|
3057
3733
|
const CONSTANT_VALUE_TS = "Vector4 | Vector3 | Matrix4 | number | (Vector4 | Matrix4)[]";
|
|
3058
3734
|
const ATTRIBUTE_VALUE_TS = "Vector4 | Vector3 | Matrix4 | number | number[]";
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3735
|
+
const ATLAS_BINDING_FILE = "gamesys/src/gamesys/scripts/script_resource.cpp";
|
|
3736
|
+
const ATLAS_INPUT_PLAYBACK_BINDING: BindingPin = {
|
|
3737
|
+
file: ATLAS_BINDING_FILE,
|
|
3738
|
+
cFunction: "MakeTextureSetFromLua",
|
|
3739
|
+
contains: "GameObjectPlaybackToDDFPlayback((dmGameObject::Playback) lua_tointeger(L,-1))",
|
|
3740
|
+
};
|
|
3741
|
+
const ATLAS_OUTPUT_PLAYBACK_BINDING: BindingPin = {
|
|
3742
|
+
file: ATLAS_BINDING_FILE,
|
|
3743
|
+
cFunction: "GetAtlas",
|
|
3744
|
+
contains: "(lua_Integer) DDFPlaybackToGameObjectPlayback(anim.m_Playback)",
|
|
3745
|
+
};
|
|
3746
|
+
|
|
3747
|
+
export interface TableFieldTypeOverride {
|
|
3748
|
+
readonly ts: string;
|
|
3749
|
+
readonly binding?: BindingPin;
|
|
3750
|
+
}
|
|
3751
|
+
|
|
3752
|
+
export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, TableFieldTypeOverride> = new Map([
|
|
3753
|
+
["compute.set_constants:param:constants:value", { ts: CONSTANT_VALUE_TS }],
|
|
3754
|
+
["material.set_constants:param:constants:value", { ts: CONSTANT_VALUE_TS }],
|
|
3755
|
+
["material.set_vertex_attributes:param:attributes:value", { ts: ATTRIBUTE_VALUE_TS }],
|
|
3756
|
+
["material.get_vertex_attributes:return:table:value", { ts: ATTRIBUTE_VALUE_TS }],
|
|
3757
|
+
[
|
|
3758
|
+
"resource.set_atlas:param:table:animations[].playback",
|
|
3759
|
+
{ ts: "go.Playback", binding: ATLAS_INPUT_PLAYBACK_BINDING },
|
|
3760
|
+
],
|
|
3761
|
+
[
|
|
3762
|
+
"resource.create_atlas:param:table:animations[].playback",
|
|
3763
|
+
{ ts: "go.Playback", binding: ATLAS_INPUT_PLAYBACK_BINDING },
|
|
3764
|
+
],
|
|
3765
|
+
[
|
|
3766
|
+
"resource.get_atlas:return:data:animations[].playback",
|
|
3767
|
+
{ ts: "go.Playback", binding: ATLAS_OUTPUT_PLAYBACK_BINDING },
|
|
3768
|
+
],
|
|
3769
|
+
["render.set_render_target:param:options:transient", { ts: "graphics.BufferType[]" }],
|
|
3770
|
+
["resource.create_sound_data:param:options:data", { ts: 'string | Opaque<"buffer">' }],
|
|
3771
|
+
// The option bags 1.13.2 types as a STRUCT, whose members still carry the
|
|
3772
|
+
// narrower upstream types: `gui.set`'s (its slot is declared in
|
|
3773
|
+
// gui-overloads.d.ts) and `resource.create_sound_data`'s.
|
|
3774
|
+
["gui.set_options:member:key", { ts: "string | Hash" }],
|
|
3775
|
+
["resource.sound_data_options:member:data", { ts: 'string | Opaque<"buffer">' }],
|
|
3074
3776
|
// Bitmasks typed as their flag enum: the create_texture example passes
|
|
3075
3777
|
// TEXTURE_USAGE_FLAG_STORAGE + TEXTURE_USAGE_FLAG_SAMPLE, a sum no single member
|
|
3076
3778
|
// names, and a reported value combines them the same way.
|
|
@@ -3081,8 +3783,15 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
3081
3783
|
"resource.render_target_attachment_info",
|
|
3082
3784
|
"material.texture_info",
|
|
3083
3785
|
] as const
|
|
3084
|
-
).map(
|
|
3085
|
-
|
|
3786
|
+
).map(
|
|
3787
|
+
(struct) => [`${struct}:member:flags`, { ts: "graphics.TextureUsageFlag | number" }] as const,
|
|
3788
|
+
),
|
|
3789
|
+
["render.render_target_buffer_params:member:flags", { ts: "render.RenderTargetFlag | number" }],
|
|
3790
|
+
// gamesys_script.cpp:PushSampler, behind both get_samplers, pushes the raw
|
|
3791
|
+
// texture type; NormalizeBindableTextureType never runs there. A separate
|
|
3792
|
+
// sampler reports TEXTURE_TYPE_SAMPLER (6) and a separate texture binding one
|
|
3793
|
+
// of the four TEXTURE_TYPE_TEXTURE_* (7-10), none of which has a Lua constant.
|
|
3794
|
+
["material.sampler_info:member:type", { ts: "graphics.TextureType | 6 | 7 | 8 | 9 | 10" }],
|
|
3086
3795
|
]);
|
|
3087
3796
|
|
|
3088
3797
|
// A field the engine accepts on an option bag whose `<dl>` upstream otherwise
|
|
@@ -3102,13 +3811,6 @@ export interface TableFieldAddition {
|
|
|
3102
3811
|
readonly evidence: readonly string[];
|
|
3103
3812
|
}
|
|
3104
3813
|
|
|
3105
|
-
const PROPERTY_OPTION_KEYS: TableField = {
|
|
3106
|
-
name: "keys",
|
|
3107
|
-
types: ["table"],
|
|
3108
|
-
optional: true,
|
|
3109
|
-
tsType: "(Hash | string)[]",
|
|
3110
|
-
};
|
|
3111
|
-
|
|
3112
3814
|
export const TABLE_SLOT_FIELD_ADDITIONS: ReadonlyMap<string, TableFieldAddition> = new Map<
|
|
3113
3815
|
string,
|
|
3114
3816
|
TableFieldAddition
|
|
@@ -3123,24 +3825,6 @@ export const TABLE_SLOT_FIELD_ADDITIONS: ReadonlyMap<string, TableFieldAddition>
|
|
|
3123
3825
|
],
|
|
3124
3826
|
},
|
|
3125
3827
|
],
|
|
3126
|
-
[
|
|
3127
|
-
"gui.get:param:options",
|
|
3128
|
-
{
|
|
3129
|
-
fields: [{ name: "key", types: ["string", "hash"], optional: true }, PROPERTY_OPTION_KEYS],
|
|
3130
|
-
evidence: [
|
|
3131
|
-
"gui_script.cpp:LuaGet reads its options through dmGameObject::LuaToPropertyOptions, which reads index, key and keys",
|
|
3132
|
-
],
|
|
3133
|
-
},
|
|
3134
|
-
],
|
|
3135
|
-
[
|
|
3136
|
-
"gui.set:param:options",
|
|
3137
|
-
{
|
|
3138
|
-
fields: [PROPERTY_OPTION_KEYS],
|
|
3139
|
-
evidence: [
|
|
3140
|
-
"gui_script.cpp:LuaSet reads its options through dmGameObject::LuaToPropertyOptions, which reads index, key and keys",
|
|
3141
|
-
],
|
|
3142
|
-
},
|
|
3143
|
-
],
|
|
3144
3828
|
]);
|
|
3145
3829
|
|
|
3146
3830
|
// A return-slot field upstream documents as platform-gated or nil-valued while
|
|
@@ -3538,12 +4222,16 @@ export function applyFieldTypeOverrides(
|
|
|
3538
4222
|
slotKind: "param" | "return" | undefined,
|
|
3539
4223
|
slotName: string | undefined,
|
|
3540
4224
|
fields: readonly TableField[],
|
|
4225
|
+
bindingEvidence: ReadonlySet<string>,
|
|
3541
4226
|
): TableField[] {
|
|
3542
4227
|
if (slotKind === undefined || slotName === undefined) return [...fields];
|
|
3543
4228
|
const prefix = `${tableSlotKey(elementName, slotKind, slotName)}:`;
|
|
3544
4229
|
const typed = (field: TableField, path: string): TableField => {
|
|
3545
|
-
const
|
|
3546
|
-
|
|
4230
|
+
const key = `${prefix}${path}`;
|
|
4231
|
+
const override = TABLE_FIELD_TYPE_OVERRIDES.get(key);
|
|
4232
|
+
return override === undefined || (override.binding !== undefined && !bindingEvidence.has(key))
|
|
4233
|
+
? field
|
|
4234
|
+
: { ...field, tsType: override.ts };
|
|
3547
4235
|
};
|
|
3548
4236
|
return fields.map((field) => {
|
|
3549
4237
|
const own = typed(field, field.name);
|
|
@@ -3890,22 +4578,84 @@ function groupFlattenedTableFields(fields: readonly TableField[]): TableField[]
|
|
|
3890
4578
|
return grouped;
|
|
3891
4579
|
}
|
|
3892
4580
|
|
|
4581
|
+
// A TYPEDEF whose aliased type the engine binding contradicts, replaced verbatim.
|
|
4582
|
+
// Member overrides reach only a STRUCT, and these alias inline LuaLS tables.
|
|
4583
|
+
// Each applies only while the typedef still aliases exactly its `upstream` tokens,
|
|
4584
|
+
// in order, so a changed upstream typedef is emitted as written rather than
|
|
4585
|
+
// shadowed; `typedef-correction-provenance.test.ts` pins every entry against the
|
|
4586
|
+
// retained ref-docs. Keyed by the typedef's ref-doc name.
|
|
4587
|
+
export interface TypedefAliasCorrection {
|
|
4588
|
+
readonly upstream: readonly string[];
|
|
4589
|
+
readonly ts: string;
|
|
4590
|
+
readonly reason: string;
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
// One arm per shape a script can build, in upstream's order, each keyed by its
|
|
4594
|
+
// own shape-type constant.
|
|
4595
|
+
const BULLET3D_SHAPE_ARMS = [
|
|
4596
|
+
["SPHERE", "diameter: number"],
|
|
4597
|
+
["BOX", "dimensions: Vector3"],
|
|
4598
|
+
["CAPSULE", "diameter: number; height: number"],
|
|
4599
|
+
["HULL", "vertices: Vector3[]"],
|
|
4600
|
+
] as const;
|
|
4601
|
+
|
|
4602
|
+
export const TYPEDEF_ALIAS_CORRECTIONS: ReadonlyMap<string, TypedefAliasCorrection> = new Map([
|
|
4603
|
+
[
|
|
4604
|
+
"render.render_target_params",
|
|
4605
|
+
{
|
|
4606
|
+
upstream: [
|
|
4607
|
+
"{ type?:graphics.TEXTURE_TYPE, sample_count?:integer, [graphics.BUFFER_TYPE]:render.render_target_buffer_params }",
|
|
4608
|
+
],
|
|
4609
|
+
ts: "{ type?: render.RenderTargetTextureType; sample_count?: number } & { [key: number]: render.RenderTargetBufferParams }",
|
|
4610
|
+
reason:
|
|
4611
|
+
'render_script.cpp:RenderScript_RenderTarget raises "Invalid render target texture type" on any `type` but graphics.TEXTURE_TYPE_2D and graphics.TEXTURE_TYPE_CUBE_MAP',
|
|
4612
|
+
},
|
|
4613
|
+
],
|
|
4614
|
+
[
|
|
4615
|
+
"bullet3d.shape.definition",
|
|
4616
|
+
{
|
|
4617
|
+
upstream: [
|
|
4618
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, diameter:number, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4619
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, dimensions:vector3, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4620
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, diameter:number, height:number, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4621
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, vertices:vector3[], position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4622
|
+
],
|
|
4623
|
+
ts: BULLET3D_SHAPE_ARMS.map(
|
|
4624
|
+
([kind, fields]) =>
|
|
4625
|
+
`{ type: typeof bullet3d.shape.SHAPE_TYPE_${kind}; ${fields}; position?: Vector3; rotation?: Quaternion; target_rotation?: Quaternion }`,
|
|
4626
|
+
).join(" | "),
|
|
4627
|
+
reason:
|
|
4628
|
+
'script_bullet3d_shape.cpp:CheckBullet3DShapeDef reads only the fields of the table\'s own `type` and raises "Unsupported shape type" on any other, a mesh included',
|
|
4629
|
+
},
|
|
4630
|
+
],
|
|
4631
|
+
]);
|
|
4632
|
+
|
|
3893
4633
|
// A handle the type map widens (`constant_buffer` takes named constants) keeps
|
|
3894
4634
|
// that shape, so annotating with the alias loses nothing the handle-returning
|
|
3895
4635
|
// function yields.
|
|
3896
4636
|
//
|
|
3897
4637
|
// An engine typedef that names its aliased type: a `userdata`/`number` handle the
|
|
3898
|
-
// type map resolves (`
|
|
4638
|
+
// type map resolves (`timer_handle`, `buffer_data`) aliases that mapping, an
|
|
3899
4639
|
// unresolved one keeps the brand, and any other aliased type (a union, literals, a
|
|
3900
|
-
// record) aliases the type itself.
|
|
4640
|
+
// record) aliases the type itself. A handle the type map resolves to a core
|
|
4641
|
+
// global (`vector` -> `Vector`, `resource_data` -> `Hash`) declares nothing, since
|
|
4642
|
+
// its slots already name the global: `null`.
|
|
3901
4643
|
function typedefAlias(
|
|
3902
4644
|
typedef: ApiTypedef,
|
|
3903
4645
|
local: string,
|
|
3904
4646
|
rawMapType: (token: string) => string,
|
|
3905
4647
|
mapType: DefoldTypeMapper,
|
|
3906
|
-
): string {
|
|
4648
|
+
): string | null {
|
|
3907
4649
|
const brand = `Opaque<"${typedef.name}">`;
|
|
3908
4650
|
const aliasOf = typedef.aliasOf ?? [];
|
|
4651
|
+
const correction = TYPEDEF_ALIAS_CORRECTIONS.get(typedef.name);
|
|
4652
|
+
if (
|
|
4653
|
+
correction !== undefined &&
|
|
4654
|
+
aliasOf.length === correction.upstream.length &&
|
|
4655
|
+
aliasOf.every((token, i) => token === correction.upstream[i])
|
|
4656
|
+
) {
|
|
4657
|
+
return `type ${local} = ${correction.ts};`;
|
|
4658
|
+
}
|
|
3909
4659
|
if (aliasOf.length > 0 && !isHandleAlias(aliasOf)) {
|
|
3910
4660
|
const armType: DefoldTypeMapper = DISCRIMINATED_MESSAGE_UNIONS.has(typedef.name)
|
|
3911
4661
|
? (token, position) => discriminatedMessageArm(token) ?? mapType(token, position)
|
|
@@ -3914,10 +4664,23 @@ function typedefAlias(
|
|
|
3914
4664
|
}
|
|
3915
4665
|
const mapped = rawMapType(typedef.name);
|
|
3916
4666
|
if (mapped.startsWith(brand)) return `type ${local} = ${mapped};`;
|
|
4667
|
+
if (aliasOf.length > 0 && CORE_GLOBAL_TYPES.has(mapped)) return null;
|
|
3917
4668
|
const resolved = aliasOf.length > 0 && mapped !== "unknown" && mapped !== local;
|
|
3918
4669
|
return `type ${local} = ${resolved ? mapped : brand};`;
|
|
3919
4670
|
}
|
|
3920
4671
|
|
|
4672
|
+
// The global types `core-types.ts` declares that the engine vocabulary maps a
|
|
4673
|
+
// handle to.
|
|
4674
|
+
const CORE_GLOBAL_TYPES: ReadonlySet<string> = new Set([
|
|
4675
|
+
"Hash",
|
|
4676
|
+
"Matrix4",
|
|
4677
|
+
"Quaternion",
|
|
4678
|
+
"Url",
|
|
4679
|
+
"Vector",
|
|
4680
|
+
"Vector3",
|
|
4681
|
+
"Vector4",
|
|
4682
|
+
]);
|
|
4683
|
+
|
|
3921
4684
|
function isHandleAlias(aliasOf: readonly string[]): boolean {
|
|
3922
4685
|
return aliasOf.length === 1 && (aliasOf[0] === "userdata" || aliasOf[0] === "number");
|
|
3923
4686
|
}
|
|
@@ -3934,8 +4697,9 @@ interface TypeDeclaration {
|
|
|
3934
4697
|
|
|
3935
4698
|
// A typedef with no aliased type keeps the flat-name rule it always had; one that
|
|
3936
4699
|
// names its aliased type, and every struct, sits at its name less the module
|
|
3937
|
-
// prefix, nested when that local is dotted.
|
|
3938
|
-
//
|
|
4700
|
+
// prefix, nested when that local is dotted. Every leaf goes through
|
|
4701
|
+
// `engineTypeName`, and `skip` matches a typedef by its reference local or its
|
|
4702
|
+
// renamed path. Typedefs come before structs, each in document order.
|
|
3939
4703
|
function collectTypeDeclarations(
|
|
3940
4704
|
module: ApiModule,
|
|
3941
4705
|
skip: ReadonlySet<string>,
|
|
@@ -3952,12 +4716,15 @@ function collectTypeDeclarations(
|
|
|
3952
4716
|
for (const t of module.typedefs) {
|
|
3953
4717
|
const local =
|
|
3954
4718
|
t.aliasOf === undefined ? (TS_IDENTIFIER.test(t.name) ? t.name : null) : localOf(t.name);
|
|
3955
|
-
if (local === null
|
|
3956
|
-
const
|
|
4719
|
+
if (local === null) continue;
|
|
4720
|
+
const path = renamedLeaf(local);
|
|
4721
|
+
if (skip.has(local) || skip.has(path)) continue;
|
|
4722
|
+
const name = path.slice(path.lastIndexOf(".") + 1);
|
|
3957
4723
|
const signature = typedefAlias(t, name, rawMapType, mapType);
|
|
4724
|
+
if (signature === null) continue;
|
|
3958
4725
|
out.push({
|
|
3959
4726
|
name,
|
|
3960
|
-
path
|
|
4727
|
+
path,
|
|
3961
4728
|
lines: (indent, decl) => [`${indent}${decl}${signature}`],
|
|
3962
4729
|
signature,
|
|
3963
4730
|
});
|
|
@@ -4015,11 +4782,12 @@ function correctedListMemberType(
|
|
|
4015
4782
|
|
|
4016
4783
|
function structDeclaration(
|
|
4017
4784
|
struct: ApiStruct,
|
|
4018
|
-
|
|
4785
|
+
local: string,
|
|
4019
4786
|
mapType: DefoldTypeMapper,
|
|
4020
4787
|
structs: readonly ApiStruct[],
|
|
4021
4788
|
bindingEvidence: ReadonlySet<string>,
|
|
4022
4789
|
): TypeDeclaration {
|
|
4790
|
+
const path = renamedLeaf(local);
|
|
4023
4791
|
const name = path.slice(path.lastIndexOf(".") + 1);
|
|
4024
4792
|
const memberPrefix = memberCorrectionKey(struct.name, "");
|
|
4025
4793
|
const declared = new Set(struct.members.map((member) => member.name));
|
|
@@ -4046,7 +4814,9 @@ function structDeclaration(
|
|
|
4046
4814
|
const members = [...struct.members, ...added].map((member) => {
|
|
4047
4815
|
const key = memberCorrectionKey(struct.name, member.name);
|
|
4048
4816
|
const list = LIST_FIELD_CORRECTIONS.get(key);
|
|
4049
|
-
const
|
|
4817
|
+
const listedRetype = TABLE_FIELD_TYPE_OVERRIDES.get(key);
|
|
4818
|
+
const retyped =
|
|
4819
|
+
listedRetype?.binding === undefined || bindingEvidence.has(key) ? listedRetype : undefined;
|
|
4050
4820
|
const optional =
|
|
4051
4821
|
(member.isOptional || OPTIONAL_MEMBER_CORRECTIONS.has(key)) &&
|
|
4052
4822
|
!REQUIRED_FIELD_CORRECTIONS.has(key);
|
|
@@ -4054,7 +4824,7 @@ function structDeclaration(
|
|
|
4054
4824
|
list !== undefined
|
|
4055
4825
|
? correctedListMemberType(struct, member, list, structs, mapType)
|
|
4056
4826
|
: retyped !== undefined
|
|
4057
|
-
? retyped
|
|
4827
|
+
? retyped.ts
|
|
4058
4828
|
: member.type === ""
|
|
4059
4829
|
? "unknown"
|
|
4060
4830
|
: mapType(member.type);
|
|
@@ -4078,7 +4848,10 @@ function structDeclaration(
|
|
|
4078
4848
|
`${indent}}`,
|
|
4079
4849
|
];
|
|
4080
4850
|
},
|
|
4081
|
-
signature:
|
|
4851
|
+
signature: interfaceSignature(
|
|
4852
|
+
name,
|
|
4853
|
+
members.map((member) => member.line),
|
|
4854
|
+
),
|
|
4082
4855
|
};
|
|
4083
4856
|
}
|
|
4084
4857
|
|
|
@@ -4137,8 +4910,9 @@ function moduleTypeMappers(
|
|
|
4137
4910
|
* dropped, so the home's own copy wins; a type sharing a value's name
|
|
4138
4911
|
* (`resource.atlas`) is untouched. A function moves the same way (1.13.2 files
|
|
4139
4912
|
* `bullet3d.collision_object.get_shapes` in the shape doc), since the emitter
|
|
4140
|
-
* declares only the functions under a module's own namespace.
|
|
4141
|
-
*
|
|
4913
|
+
* declares only the functions under a module's own namespace. A moved type
|
|
4914
|
+
* records the module that declared it as `declaredIn`. Modules with nothing
|
|
4915
|
+
* moved in or out are returned as-is.
|
|
4142
4916
|
*/
|
|
4143
4917
|
export function routeTypeDeclarations(modules: readonly ApiModule[]): ApiModule[] {
|
|
4144
4918
|
const homeOf = (name: string, declaring: string): string => {
|
|
@@ -4168,14 +4942,14 @@ export function routeTypeDeclarations(modules: readonly ApiModule[]): ApiModule[
|
|
|
4168
4942
|
const structs = (module.structs ?? []).filter((struct) => {
|
|
4169
4943
|
const home = homeOf(struct.name, module.namespace);
|
|
4170
4944
|
if (home === module.namespace) return true;
|
|
4171
|
-
into(home).structs.push(struct);
|
|
4945
|
+
into(home).structs.push({ ...struct, declaredIn: module.namespace });
|
|
4172
4946
|
return false;
|
|
4173
4947
|
});
|
|
4174
4948
|
const typedefs = module.typedefs.filter((typedef) => {
|
|
4175
4949
|
if (typedef.aliasOf === undefined) return true;
|
|
4176
4950
|
const home = homeOf(typedef.name, module.namespace);
|
|
4177
4951
|
if (home === module.namespace) return true;
|
|
4178
|
-
into(home).typedefs.push(typedef);
|
|
4952
|
+
into(home).typedefs.push({ ...typedef, declaredIn: module.namespace });
|
|
4179
4953
|
return false;
|
|
4180
4954
|
});
|
|
4181
4955
|
const functions = module.functions.filter((fn) => {
|
|
@@ -4218,27 +4992,32 @@ export function routeTypeDeclarations(modules: readonly ApiModule[]): ApiModule[
|
|
|
4218
4992
|
/**
|
|
4219
4993
|
* The TS path of every `ENUM`, `STRUCT` and aliasing `TYPEDEF` the (routed)
|
|
4220
4994
|
* modules declare, keyed by its Lua name: the name itself when it sits under its
|
|
4221
|
-
* module's namespace, else that name nested under the module
|
|
4222
|
-
* (`on_input.action` in `go` is `go.on_input.
|
|
4995
|
+
* module's namespace, else that name nested under the module, with its leaf
|
|
4996
|
+
* through `engineTypeName` (`on_input.action` in `go` is `go.on_input.Action`).
|
|
4223
4997
|
*/
|
|
4224
4998
|
export function declaredTypePaths(modules: readonly ApiModule[]): Map<string, string> {
|
|
4225
4999
|
const paths = new Map<string, string>();
|
|
4226
5000
|
for (const module of modules) {
|
|
4227
|
-
const
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
...module.
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
name.startsWith(`${module.namespace}.`) ? name : `${module.namespace}.${name}`,
|
|
4236
|
-
);
|
|
5001
|
+
const qualified = (name: string): string =>
|
|
5002
|
+
name.startsWith(`${module.namespace}.`) ? name : `${module.namespace}.${name}`;
|
|
5003
|
+
for (const { name } of [
|
|
5004
|
+
...(module.enums ?? []),
|
|
5005
|
+
...(module.structs ?? []),
|
|
5006
|
+
...module.typedefs.filter((t) => t.aliasOf !== undefined),
|
|
5007
|
+
]) {
|
|
5008
|
+
paths.set(name, renamedLeaf(qualified(name)));
|
|
4237
5009
|
}
|
|
4238
5010
|
}
|
|
4239
5011
|
return paths;
|
|
4240
5012
|
}
|
|
4241
5013
|
|
|
5014
|
+
// A dotted type path with its last segment through `engineTypeName`: the
|
|
5015
|
+
// namespace segments mirror the engine's functions and stay as they are.
|
|
5016
|
+
function renamedLeaf(path: string): string {
|
|
5017
|
+
const dot = path.lastIndexOf(".");
|
|
5018
|
+
return `${path.slice(0, dot + 1)}${engineTypeName(path.slice(dot + 1))}`;
|
|
5019
|
+
}
|
|
5020
|
+
|
|
4242
5021
|
export function emitDeclarations(documented: ApiModule, options?: EmitOptions): string {
|
|
4243
5022
|
const module = withUndocumentedConstants(documented, options?.registeredConstants);
|
|
4244
5023
|
const prefix = `${module.namespace}.`;
|
|
@@ -4248,11 +5027,13 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4248
5027
|
const translations = options?.translations ?? {};
|
|
4249
5028
|
const urlParameters = options?.urlParameters ?? [];
|
|
4250
5029
|
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
5030
|
+
const nilRaisingSlots = options?.nilRaisingSlots;
|
|
4251
5031
|
const baseNotes = indexBaseNoteResolver(options?.indexBaseSource ?? "engine");
|
|
4252
5032
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
4253
5033
|
const constantTokens = constantSlotTokenResolver(
|
|
4254
5034
|
module,
|
|
4255
5035
|
new Set([...constantsByFqn.keys(), ...(knownConstantFqns ?? [])]),
|
|
5036
|
+
options?.withheldFunctions,
|
|
4256
5037
|
);
|
|
4257
5038
|
|
|
4258
5039
|
const constants = module.constants
|
|
@@ -4285,6 +5066,13 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4285
5066
|
const nestedRoot = collectNestedGroups(module, prefix);
|
|
4286
5067
|
const enumAliases = collectEnumAliases(module, constants, nestedRoot);
|
|
4287
5068
|
|
|
5069
|
+
const resolver = buildTableDocResolver(
|
|
5070
|
+
module.functions.map((fn) => ({
|
|
5071
|
+
name: fn.name,
|
|
5072
|
+
slots: [...fn.parameters, ...fn.returnValues],
|
|
5073
|
+
})),
|
|
5074
|
+
);
|
|
5075
|
+
|
|
4288
5076
|
// Colon methods (`client:send`) are FUNCTION elements named `<receiver>:<method>`
|
|
4289
5077
|
// and are NOT namespace-prefixed, so they fail the flat-identifier test in
|
|
4290
5078
|
// prepareFunction (the colon) and are absent from `functions`. A returned handle
|
|
@@ -4292,27 +5080,26 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4292
5080
|
// interface, the same non-flat recovery the dns nested-namespace pass does one
|
|
4293
5081
|
// container shape shallower.
|
|
4294
5082
|
const handleGroups = collectHandleMethodGroups(module);
|
|
4295
|
-
const
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
5083
|
+
const handleInterfaces = collectHandleInterfaces(
|
|
5084
|
+
module.namespace,
|
|
5085
|
+
handleGroups,
|
|
5086
|
+
mapType,
|
|
5087
|
+
resolver,
|
|
5088
|
+
constantTokens,
|
|
5089
|
+
urlParameters,
|
|
5090
|
+
bindingEvidence,
|
|
4302
5091
|
);
|
|
4303
5092
|
|
|
4304
5093
|
// A typedef that also has colon methods is emitted as a method-bearing
|
|
4305
5094
|
// interface below, not an opaque brand alias.
|
|
4306
|
-
const
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
bindingEvidence,
|
|
4313
|
-
),
|
|
4314
|
-
nestedRoot,
|
|
5095
|
+
const collectedTypes = collectTypeDeclarations(
|
|
5096
|
+
module,
|
|
5097
|
+
handleTypedefNames(module.namespace, handleGroups.keys()),
|
|
5098
|
+
rawMapType,
|
|
5099
|
+
mapType,
|
|
5100
|
+
bindingEvidence,
|
|
4315
5101
|
);
|
|
5102
|
+
const typeDeclarations = placeTypeDeclarations(collectedTypes, nestedRoot);
|
|
4316
5103
|
|
|
4317
5104
|
// A re-export alias (`export { _x as x }`) switches the ambient namespace out
|
|
4318
5105
|
// of its implicit-export mode, so once any alias is present every sibling
|
|
@@ -4329,18 +5116,22 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4329
5116
|
lines.push(`declare namespace ${module.namespace} {`);
|
|
4330
5117
|
|
|
4331
5118
|
for (const declaration of typeDeclarations) lines.push(...declaration.lines(INDENT, decl));
|
|
4332
|
-
for (const aliasDecl of constantUnionAliasDeclarations(
|
|
5119
|
+
for (const aliasDecl of constantUnionAliasDeclarations(
|
|
5120
|
+
module.namespace,
|
|
5121
|
+
new Set(enumAliases.all.map((alias) => alias.path)),
|
|
5122
|
+
)) {
|
|
4333
5123
|
lines.push(`${INDENT}${decl}${aliasDecl}`);
|
|
4334
5124
|
}
|
|
4335
|
-
for (const
|
|
5125
|
+
for (const { declaration } of overloadSlotAliasDeclarations(
|
|
4336
5126
|
[...module.functions, ...(options?.withheldFunctions ?? [])],
|
|
5127
|
+
collectedTypes,
|
|
4337
5128
|
prefix,
|
|
4338
5129
|
mapType,
|
|
4339
5130
|
constantTokens,
|
|
4340
5131
|
urlParameters,
|
|
4341
5132
|
bindingEvidence,
|
|
4342
5133
|
)) {
|
|
4343
|
-
lines.push(`${INDENT}${decl}${
|
|
5134
|
+
lines.push(`${INDENT}${decl}${declaration}`);
|
|
4344
5135
|
}
|
|
4345
5136
|
for (const alias of enumAliases.topLevel) {
|
|
4346
5137
|
lines.push(
|
|
@@ -4348,16 +5139,13 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4348
5139
|
);
|
|
4349
5140
|
}
|
|
4350
5141
|
const handleIndent = `${INDENT}${INDENT}`;
|
|
4351
|
-
for (const
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
for (const fn of group) {
|
|
5142
|
+
for (const { name, members } of handleInterfaces) {
|
|
5143
|
+
lines.push(`${INDENT}${decl}interface ${name} {`);
|
|
5144
|
+
for (const { fn, text } of members) {
|
|
4355
5145
|
for (const docLine of functionDocLines(fn.original, translations, baseNotes, handleIndent)) {
|
|
4356
5146
|
lines.push(docLine);
|
|
4357
5147
|
}
|
|
4358
|
-
lines.push(
|
|
4359
|
-
`${handleIndent}${emitMethod(fn, mapType, resolver, constantTokens, urlParameters, bindingEvidence)}`,
|
|
4360
|
-
);
|
|
5148
|
+
lines.push(`${handleIndent}${text}`);
|
|
4361
5149
|
}
|
|
4362
5150
|
lines.push(`${INDENT}}`);
|
|
4363
5151
|
}
|
|
@@ -4385,18 +5173,20 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4385
5173
|
for (const fn of functions) {
|
|
4386
5174
|
const reserved = TS_RESERVED_NAMES.has(fn.name);
|
|
4387
5175
|
const emitName = aliasName(fn.name, aliases);
|
|
4388
|
-
for (const
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
5176
|
+
for (const arm of callArms(fn, nilRaisingSlots, module.structs ?? [])) {
|
|
5177
|
+
for (const docLine of functionDocLines(fn.original, translations, baseNotes))
|
|
5178
|
+
lines.push(docLine);
|
|
5179
|
+
const line = emitFunction(
|
|
5180
|
+
arm,
|
|
5181
|
+
emitName,
|
|
5182
|
+
mapType,
|
|
5183
|
+
resolver,
|
|
5184
|
+
constantTokens,
|
|
5185
|
+
urlParameters,
|
|
5186
|
+
bindingEvidence,
|
|
5187
|
+
);
|
|
5188
|
+
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
5189
|
+
}
|
|
4400
5190
|
const alternative = firstSlotAlternative(fn, module.namespace);
|
|
4401
5191
|
if (alternative !== null) {
|
|
4402
5192
|
// The overload documents its own parameters, so its hover names the base
|
|
@@ -4501,9 +5291,9 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4501
5291
|
|
|
4502
5292
|
if (module.properties.length > 0) {
|
|
4503
5293
|
const members = [...module.properties].sort((a, b) => a.name.localeCompare(b.name));
|
|
4504
|
-
lines.push(`${INDENT}${decl}interface
|
|
5294
|
+
lines.push(`${INDENT}${decl}interface ${PROPERTIES_INTERFACE} {`);
|
|
4505
5295
|
for (const p of members) {
|
|
4506
|
-
for (const member of emitPropertyMembers(p, mapType, module.namespace)) {
|
|
5296
|
+
for (const member of emitPropertyMembers(p, mapType, module.namespace, bindingEvidence)) {
|
|
4507
5297
|
for (const docLine of summaryDocLines(p.brief, p.description, `${INDENT}${INDENT}`)) {
|
|
4508
5298
|
lines.push(docLine);
|
|
4509
5299
|
}
|
|
@@ -4518,17 +5308,25 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4518
5308
|
}
|
|
4519
5309
|
|
|
4520
5310
|
// One `type <alias> = <slot type>;` per `OVERLOAD_SLOT_ALIASES` slot these
|
|
4521
|
-
// functions carry, in alias order
|
|
4522
|
-
//
|
|
4523
|
-
//
|
|
5311
|
+
// functions carry, in alias order, skipping an alias one of the module's
|
|
5312
|
+
// top-level type `declarations` already names. The type is the one
|
|
5313
|
+
// `emitFunction` reports for the slot, so it reads as the function's own
|
|
5314
|
+
// signature would. The resolver spans these functions alone, leaving the
|
|
5315
|
+
// module's own untouched.
|
|
4524
5316
|
function overloadSlotAliasDeclarations(
|
|
4525
5317
|
functions: readonly ApiFunction[],
|
|
5318
|
+
declarations: readonly TypeDeclaration[],
|
|
4526
5319
|
prefix: string,
|
|
4527
5320
|
mapType: DefoldTypeMapper,
|
|
4528
5321
|
constantTokens: ConstantSlotTokens,
|
|
4529
5322
|
urlParameters: UrlParameterTable,
|
|
4530
5323
|
bindingEvidence: ReadonlySet<string>,
|
|
4531
|
-
): string[] {
|
|
5324
|
+
): { name: string; declaration: string }[] {
|
|
5325
|
+
const declared = new Set(
|
|
5326
|
+
declarations
|
|
5327
|
+
.filter((declaration) => declaration.path === declaration.name)
|
|
5328
|
+
.map((declaration) => declaration.name),
|
|
5329
|
+
);
|
|
4532
5330
|
const resolver = buildTableDocResolver(
|
|
4533
5331
|
functions.map((fn) => ({ name: fn.name, slots: [...fn.parameters, ...fn.returnValues] })),
|
|
4534
5332
|
);
|
|
@@ -4536,7 +5334,7 @@ function overloadSlotAliasDeclarations(
|
|
|
4536
5334
|
for (const fn of functions) {
|
|
4537
5335
|
for (const [position, p] of fn.parameters.entries()) {
|
|
4538
5336
|
const alias = OVERLOAD_SLOT_ALIASES.get(tableSlotKey(fn.name, "param", p.name));
|
|
4539
|
-
if (alias === undefined) continue;
|
|
5337
|
+
if (alias === undefined || declared.has(alias)) continue;
|
|
4540
5338
|
const prepared = prepareFunction(fn, prefix);
|
|
4541
5339
|
if (prepared === null) continue;
|
|
4542
5340
|
const slots: Record<string, SlotType> = {};
|
|
@@ -4563,7 +5361,51 @@ function overloadSlotAliasDeclarations(
|
|
|
4563
5361
|
}
|
|
4564
5362
|
return [...types]
|
|
4565
5363
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
4566
|
-
.map(([
|
|
5364
|
+
.map(([name, type]) => ({ name, declaration: `type ${name} = ${type};` }));
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
// A method of a handle interface: the member text the declarations print inside
|
|
5368
|
+
// the interface, which its FUNCTION entry signs too.
|
|
5369
|
+
interface HandleMember {
|
|
5370
|
+
fn: PreparedFunction;
|
|
5371
|
+
text: string;
|
|
5372
|
+
slotTypes: SlotTypes;
|
|
5373
|
+
}
|
|
5374
|
+
|
|
5375
|
+
// Each handle interface a module's colon methods declare, sorted by name, with
|
|
5376
|
+
// its methods in group order.
|
|
5377
|
+
function collectHandleInterfaces(
|
|
5378
|
+
namespace: string,
|
|
5379
|
+
handleGroups: ReadonlyMap<string, readonly PreparedFunction[]>,
|
|
5380
|
+
mapType: DefoldTypeMapper,
|
|
5381
|
+
resolver: TableDocResolver,
|
|
5382
|
+
constantTokens: ConstantSlotTokens,
|
|
5383
|
+
urlParameters: UrlParameterTable,
|
|
5384
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5385
|
+
): { name: string; members: HandleMember[] }[] {
|
|
5386
|
+
return [...handleGroups]
|
|
5387
|
+
.map(([receiver, group]) => ({
|
|
5388
|
+
name: handleInterfaceName(namespace, receiver),
|
|
5389
|
+
members: group.map((fn): HandleMember => {
|
|
5390
|
+
const slotTypes: Record<string, SlotType> = {};
|
|
5391
|
+
const text = emitMethod(
|
|
5392
|
+
fn,
|
|
5393
|
+
mapType,
|
|
5394
|
+
resolver,
|
|
5395
|
+
constantTokens,
|
|
5396
|
+
urlParameters,
|
|
5397
|
+
bindingEvidence,
|
|
5398
|
+
slotTypes,
|
|
5399
|
+
);
|
|
5400
|
+
return { fn, text, slotTypes };
|
|
5401
|
+
}),
|
|
5402
|
+
}))
|
|
5403
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
// An interface signed on one line, each member line followed by a space.
|
|
5407
|
+
function interfaceSignature(name: string, members: readonly string[]): string {
|
|
5408
|
+
return `interface ${name} { ${members.map((member) => `${member} `).join("")}}`;
|
|
4567
5409
|
}
|
|
4568
5410
|
|
|
4569
5411
|
/**
|
|
@@ -4620,10 +5462,12 @@ export function emitSymbolSignatures(
|
|
|
4620
5462
|
const knownConstantFqns = options?.knownConstantFqns;
|
|
4621
5463
|
const urlParameters = options?.urlParameters ?? [];
|
|
4622
5464
|
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
5465
|
+
const nilRaisingSlots = options?.nilRaisingSlots;
|
|
4623
5466
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
4624
5467
|
const constantTokens = constantSlotTokenResolver(
|
|
4625
5468
|
module,
|
|
4626
5469
|
new Set([...constantsByFqn.keys(), ...(knownConstantFqns ?? [])]),
|
|
5470
|
+
options?.withheldFunctions,
|
|
4627
5471
|
);
|
|
4628
5472
|
const resolver = buildTableDocResolver(
|
|
4629
5473
|
module.functions.map((fn) => ({
|
|
@@ -4650,8 +5494,9 @@ export function emitSymbolSignatures(
|
|
|
4650
5494
|
const prepared = prepareFunction(fn, prefix);
|
|
4651
5495
|
if (prepared === null) continue;
|
|
4652
5496
|
const slotTypes: Record<string, SlotType> = {};
|
|
5497
|
+
const [first = prepared, ...later] = callArms(prepared, nilRaisingSlots, module.structs ?? []);
|
|
4653
5498
|
const primary = emitFunction(
|
|
4654
|
-
|
|
5499
|
+
first,
|
|
4655
5500
|
emitName(prepared.name),
|
|
4656
5501
|
mapType,
|
|
4657
5502
|
resolver,
|
|
@@ -4661,18 +5506,18 @@ export function emitSymbolSignatures(
|
|
|
4661
5506
|
slotTypes,
|
|
4662
5507
|
);
|
|
4663
5508
|
const alternative = firstSlotAlternative(prepared, module.namespace);
|
|
4664
|
-
const
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
5509
|
+
const arms = [...later, ...(alternative === null ? [] : [alternative])].map((arm) =>
|
|
5510
|
+
emitFunction(
|
|
5511
|
+
arm,
|
|
5512
|
+
emitName(prepared.name),
|
|
5513
|
+
mapType,
|
|
5514
|
+
resolver,
|
|
5515
|
+
constantTokens,
|
|
5516
|
+
urlParameters,
|
|
5517
|
+
bindingEvidence,
|
|
5518
|
+
),
|
|
5519
|
+
);
|
|
5520
|
+
const tsSignature = [primary, ...arms].join("\n");
|
|
4676
5521
|
out.push({ identity: fnIdentity(fn), tsSignature, slotTypes });
|
|
4677
5522
|
}
|
|
4678
5523
|
|
|
@@ -4720,24 +5565,29 @@ export function emitSymbolSignatures(
|
|
|
4720
5565
|
});
|
|
4721
5566
|
}
|
|
4722
5567
|
|
|
5568
|
+
// A handle interface signs as one line, as a struct does; each member is the
|
|
5569
|
+
// text its method's FUNCTION entry signs.
|
|
4723
5570
|
const handleGroups = collectHandleMethodGroups(module);
|
|
4724
|
-
for (const
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
bindingEvidence,
|
|
4736
|
-
slotTypes,
|
|
4737
|
-
),
|
|
4738
|
-
slotTypes,
|
|
4739
|
-
});
|
|
5571
|
+
for (const { name, members } of collectHandleInterfaces(
|
|
5572
|
+
module.namespace,
|
|
5573
|
+
handleGroups,
|
|
5574
|
+
mapType,
|
|
5575
|
+
resolver,
|
|
5576
|
+
constantTokens,
|
|
5577
|
+
urlParameters,
|
|
5578
|
+
bindingEvidence,
|
|
5579
|
+
)) {
|
|
5580
|
+
for (const { fn, text, slotTypes } of members) {
|
|
5581
|
+
out.push({ identity: fnIdentity(fn.original), tsSignature: text, slotTypes });
|
|
4740
5582
|
}
|
|
5583
|
+
out.push({
|
|
5584
|
+
identity: { namespace: module.namespace, kind: "TYPEDEF", name, signature: "" },
|
|
5585
|
+
tsSignature: interfaceSignature(
|
|
5586
|
+
name,
|
|
5587
|
+
members.map((member) => member.text),
|
|
5588
|
+
),
|
|
5589
|
+
slotTypes: NO_SLOTS,
|
|
5590
|
+
});
|
|
4741
5591
|
}
|
|
4742
5592
|
|
|
4743
5593
|
const constants: PreparedConstant[] = [];
|
|
@@ -4766,7 +5616,7 @@ export function emitSymbolSignatures(
|
|
|
4766
5616
|
// carries the first, which is verbatim in the `.d.ts` and is what the `/api`
|
|
4767
5617
|
// render layer reduces to `<placeholder>: <type>`.
|
|
4768
5618
|
for (const p of module.properties) {
|
|
4769
|
-
const [firstMember = ""] = emitPropertyMembers(p, mapType, module.namespace);
|
|
5619
|
+
const [firstMember = ""] = emitPropertyMembers(p, mapType, module.namespace, bindingEvidence);
|
|
4770
5620
|
out.push({
|
|
4771
5621
|
identity: { namespace: module.namespace, kind: "PROPERTY", name: p.name, signature: "" },
|
|
4772
5622
|
tsSignature: firstMember,
|
|
@@ -4776,13 +5626,14 @@ export function emitSymbolSignatures(
|
|
|
4776
5626
|
|
|
4777
5627
|
// A struct signs as its interface on one line; each member line is verbatim in
|
|
4778
5628
|
// the declarations.
|
|
4779
|
-
|
|
5629
|
+
const typeDeclarations = collectTypeDeclarations(
|
|
4780
5630
|
module,
|
|
4781
|
-
|
|
5631
|
+
handleTypedefNames(module.namespace, handleGroups.keys()),
|
|
4782
5632
|
rawMapType,
|
|
4783
5633
|
mapType,
|
|
4784
5634
|
bindingEvidence,
|
|
4785
|
-
)
|
|
5635
|
+
);
|
|
5636
|
+
for (const declaration of typeDeclarations) {
|
|
4786
5637
|
out.push({
|
|
4787
5638
|
identity: {
|
|
4788
5639
|
namespace: module.namespace,
|
|
@@ -4795,23 +5646,50 @@ export function emitSymbolSignatures(
|
|
|
4795
5646
|
});
|
|
4796
5647
|
}
|
|
4797
5648
|
|
|
5649
|
+
// An overload-slot alias is declared exactly where the declarations carry it,
|
|
5650
|
+
// so it signs from the same functions and the same declared set.
|
|
5651
|
+
for (const { name, declaration } of overloadSlotAliasDeclarations(
|
|
5652
|
+
[...module.functions, ...(options?.withheldFunctions ?? [])],
|
|
5653
|
+
typeDeclarations,
|
|
5654
|
+
prefix,
|
|
5655
|
+
mapType,
|
|
5656
|
+
constantTokens,
|
|
5657
|
+
urlParameters,
|
|
5658
|
+
bindingEvidence,
|
|
5659
|
+
)) {
|
|
5660
|
+
out.push({
|
|
5661
|
+
identity: { namespace: module.namespace, kind: "TYPEDEF", name, signature: "" },
|
|
5662
|
+
tsSignature: declaration,
|
|
5663
|
+
slotTypes: NO_SLOTS,
|
|
5664
|
+
});
|
|
5665
|
+
}
|
|
5666
|
+
|
|
4798
5667
|
// The module's constant-union aliases travel as typedefs too: a signature that
|
|
4799
5668
|
// names one carries no member list of its own, so the docs read the members
|
|
4800
5669
|
// from here rather than from the emitter's table.
|
|
4801
|
-
|
|
5670
|
+
const enumAliases = collectEnumAliases(module, constants, nestedRoot);
|
|
5671
|
+
for (const row of ownedConstantUnionAliases(
|
|
5672
|
+
module.namespace,
|
|
5673
|
+
new Set(enumAliases.all.map((alias) => alias.path)),
|
|
5674
|
+
)) {
|
|
5675
|
+
const name = engineTypeName(row.name);
|
|
4802
5676
|
out.push({
|
|
4803
|
-
identity: { namespace: module.namespace, kind: "TYPEDEF", name
|
|
4804
|
-
tsSignature: unionAliasDeclaration(
|
|
5677
|
+
identity: { namespace: module.namespace, kind: "TYPEDEF", name, signature: "" },
|
|
5678
|
+
tsSignature: unionAliasDeclaration(name, row.members),
|
|
4805
5679
|
slotTypes: NO_SLOTS,
|
|
4806
5680
|
});
|
|
4807
5681
|
}
|
|
4808
5682
|
|
|
4809
|
-
// An enum alias is keyed by its local path (`
|
|
4810
|
-
// constant-union aliases above are keyed by their
|
|
4811
|
-
const enumAliases = collectEnumAliases(module, constants, nestedRoot);
|
|
5683
|
+
// An enum alias is keyed by its declared local path (`Status`, `ui.Color`),
|
|
5684
|
+
// as the constant-union aliases above are keyed by their declared name.
|
|
4812
5685
|
for (const alias of enumAliases.all) {
|
|
4813
5686
|
out.push({
|
|
4814
|
-
identity: {
|
|
5687
|
+
identity: {
|
|
5688
|
+
namespace: module.namespace,
|
|
5689
|
+
kind: "TYPEDEF",
|
|
5690
|
+
name: renamedLeaf(alias.path),
|
|
5691
|
+
signature: "",
|
|
5692
|
+
},
|
|
4815
5693
|
tsSignature: unionAliasDeclaration(alias.name, alias.members, alias.nilable),
|
|
4816
5694
|
slotTypes: NO_SLOTS,
|
|
4817
5695
|
});
|
|
@@ -4829,6 +5707,12 @@ interface PreparedConstant {
|
|
|
4829
5707
|
interface PreparedFunction {
|
|
4830
5708
|
name: string;
|
|
4831
5709
|
original: ApiFunction;
|
|
5710
|
+
// Parameter positions declared required whatever their doc says: the
|
|
5711
|
+
// nil-raising slots an arm of `nilRaisingArms` takes.
|
|
5712
|
+
required?: ReadonlySet<number>;
|
|
5713
|
+
// A rewrite of the mapped type at a parameter position: the fields an arm of
|
|
5714
|
+
// `presenceArms` requires.
|
|
5715
|
+
retype?: ReadonlyMap<number, (type: string) => string>;
|
|
4832
5716
|
}
|
|
4833
5717
|
|
|
4834
5718
|
interface PreparedVariable {
|
|
@@ -4847,8 +5731,9 @@ interface NestedGroup {
|
|
|
4847
5731
|
children: Map<string, NestedGroup>;
|
|
4848
5732
|
}
|
|
4849
5733
|
|
|
4850
|
-
// One `ENUM`'s union alias: `name` is the declared identifier, `path`
|
|
4851
|
-
// path under the module namespace (`ui.COLOR`), `members`
|
|
5734
|
+
// One `ENUM`'s union alias: `name` is the declared identifier (`Color`), `path`
|
|
5735
|
+
// the reference's local path under the module namespace (`ui.COLOR`), `members`
|
|
5736
|
+
// the member FQNs.
|
|
4852
5737
|
interface EnumAlias {
|
|
4853
5738
|
name: string;
|
|
4854
5739
|
path: string;
|
|
@@ -4977,12 +5862,14 @@ function collectEnumAliases(
|
|
|
4977
5862
|
const nilable = new Set(module.constants.filter((c) => c.nilable).map((c) => c.name));
|
|
4978
5863
|
const topLevel: EnumAlias[] = [];
|
|
4979
5864
|
const all: EnumAlias[] = [];
|
|
4980
|
-
const enums = [...(module.enums ?? [])].sort((a, b) =>
|
|
5865
|
+
const enums = [...(module.enums ?? [])].sort((a, b) =>
|
|
5866
|
+
renamedLeaf(a.name).localeCompare(renamedLeaf(b.name)),
|
|
5867
|
+
);
|
|
4981
5868
|
for (const e of enums) {
|
|
4982
5869
|
if (e.members.length === 0 || !e.members.every((m) => declared.has(m))) continue;
|
|
4983
5870
|
const path = stripPrefix(e.name, prefix);
|
|
4984
5871
|
if (TS_IDENTIFIER.test(path)) {
|
|
4985
|
-
const alias = { name: path, path, members: e.members, nilable };
|
|
5872
|
+
const alias = { name: engineTypeName(path), path, members: e.members, nilable };
|
|
4986
5873
|
topLevel.push(alias);
|
|
4987
5874
|
all.push(alias);
|
|
4988
5875
|
continue;
|
|
@@ -4998,7 +5885,7 @@ function collectEnumAliases(
|
|
|
4998
5885
|
level = group.children;
|
|
4999
5886
|
}
|
|
5000
5887
|
if (group === undefined) continue;
|
|
5001
|
-
const alias = { name, path, members: e.members, nilable };
|
|
5888
|
+
const alias = { name: engineTypeName(name), path, members: e.members, nilable };
|
|
5002
5889
|
group.enumAliases.push(alias);
|
|
5003
5890
|
all.push(alias);
|
|
5004
5891
|
}
|
|
@@ -5112,10 +5999,13 @@ export const NIL_CONSTANTS: ReadonlyMap<string, string> = new Map([
|
|
|
5112
5999
|
// `undocumented-constant-provenance.test.ts` reds an entry no committed target
|
|
5113
6000
|
// still needs. `sameValueAs` names the documented constant the engine registers
|
|
5114
6001
|
// with the same value; where the surface declares it, the back-fill takes its
|
|
5115
|
-
// type, so a value compares equal to either spelling.
|
|
6002
|
+
// type, so a value compares equal to either spelling. `memberOf` names the
|
|
6003
|
+
// ref-doc `ENUM` the engine returns the constant through; the back-fill joins it
|
|
6004
|
+
// only on a surface whose ref-doc declares that enum, so an older surface
|
|
6005
|
+
// without one keeps a plain constant.
|
|
5116
6006
|
export const UNDOCUMENTED_CONSTANTS: ReadonlyMap<
|
|
5117
6007
|
string,
|
|
5118
|
-
{ readonly doc: string; readonly sameValueAs?: string }
|
|
6008
|
+
{ readonly doc: string; readonly sameValueAs?: string; readonly memberOf?: string }
|
|
5119
6009
|
> = new Map([
|
|
5120
6010
|
[
|
|
5121
6011
|
"render.CONTEXT_EVENT_CONTEXT_LOST",
|
|
@@ -5136,6 +6026,13 @@ export const UNDOCUMENTED_CONSTANTS: ReadonlyMap<
|
|
|
5136
6026
|
sameValueAs: "window.WINDOW_EVENT_ICONIFIED",
|
|
5137
6027
|
},
|
|
5138
6028
|
],
|
|
6029
|
+
[
|
|
6030
|
+
"liveupdate.LIVEUPDATE_NOT_INITIALIZED",
|
|
6031
|
+
{
|
|
6032
|
+
doc: "Live update is not initialized. `liveupdate.add_mount` and `liveupdate.remove_mount` return it while live update is disabled.",
|
|
6033
|
+
memberOf: "liveupdate.LIVEUPDATE",
|
|
6034
|
+
},
|
|
6035
|
+
],
|
|
5139
6036
|
]);
|
|
5140
6037
|
|
|
5141
6038
|
function withUndocumentedConstants(
|
|
@@ -5145,7 +6042,8 @@ function withUndocumentedConstants(
|
|
|
5145
6042
|
const prefix = `${module.namespace}.`;
|
|
5146
6043
|
const documented = new Set(module.constants.map((c) => c.name));
|
|
5147
6044
|
const backfill: ApiConstant[] = [];
|
|
5148
|
-
|
|
6045
|
+
const joined = new Map<string, string[]>();
|
|
6046
|
+
for (const [fqn, { doc, sameValueAs, memberOf }] of UNDOCUMENTED_CONSTANTS) {
|
|
5149
6047
|
if (!fqn.startsWith(prefix) || fqn.slice(prefix.length).includes(".")) continue;
|
|
5150
6048
|
if (documented.has(fqn)) continue;
|
|
5151
6049
|
if (registered !== undefined && !registered.has(fqn)) continue;
|
|
@@ -5157,10 +6055,18 @@ function withUndocumentedConstants(
|
|
|
5157
6055
|
description: "",
|
|
5158
6056
|
...(aliasOf === undefined ? {} : { aliasOf }),
|
|
5159
6057
|
});
|
|
6058
|
+
if (memberOf !== undefined) joined.set(memberOf, [...(joined.get(memberOf) ?? []), fqn]);
|
|
5160
6059
|
}
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
6060
|
+
if (backfill.length === 0) return module;
|
|
6061
|
+
const enums = module.enums?.map((e) => {
|
|
6062
|
+
const members = joined.get(e.name);
|
|
6063
|
+
return members === undefined ? e : { ...e, members: [...e.members, ...members] };
|
|
6064
|
+
});
|
|
6065
|
+
return {
|
|
6066
|
+
...module,
|
|
6067
|
+
constants: [...module.constants, ...backfill],
|
|
6068
|
+
...(enums === undefined ? {} : { enums }),
|
|
6069
|
+
};
|
|
5164
6070
|
}
|
|
5165
6071
|
|
|
5166
6072
|
// `constant` is the module's own element for `fqn`, absent for a constant another
|
|
@@ -5244,10 +6150,26 @@ function memberSignature(
|
|
|
5244
6150
|
): string {
|
|
5245
6151
|
const original = prepared.original.parameters;
|
|
5246
6152
|
const elementName = prepared.original.name;
|
|
5247
|
-
const
|
|
6153
|
+
const required = prepared.required ?? new Set<number>();
|
|
6154
|
+
const cutoff = Math.max(
|
|
6155
|
+
trailingOptionalCutoff(original, elementName),
|
|
6156
|
+
...[...required].map((index) => index + 1),
|
|
6157
|
+
);
|
|
5248
6158
|
const varargIndex = original.findIndex(isVarargParameter);
|
|
5249
6159
|
const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
|
|
5250
|
-
emitParameter(
|
|
6160
|
+
emitParameter(
|
|
6161
|
+
p,
|
|
6162
|
+
i,
|
|
6163
|
+
i >= cutoff,
|
|
6164
|
+
mapType,
|
|
6165
|
+
resolver,
|
|
6166
|
+
constantTokens,
|
|
6167
|
+
elementName,
|
|
6168
|
+
urlParameters,
|
|
6169
|
+
bindingEvidence,
|
|
6170
|
+
required.has(i),
|
|
6171
|
+
prepared.retype?.get(i),
|
|
6172
|
+
),
|
|
5251
6173
|
);
|
|
5252
6174
|
const rest =
|
|
5253
6175
|
varargIndex === -1
|
|
@@ -5260,6 +6182,7 @@ function memberSignature(
|
|
|
5260
6182
|
constantTokens,
|
|
5261
6183
|
elementName,
|
|
5262
6184
|
urlParameters,
|
|
6185
|
+
bindingEvidence,
|
|
5263
6186
|
);
|
|
5264
6187
|
const params = [...positional.map((p) => p.text), ...(rest ? [rest.text] : [])].join(", ");
|
|
5265
6188
|
const ret = emitReturn(
|
|
@@ -5296,7 +6219,7 @@ function memberSignature(
|
|
|
5296
6219
|
if (isPredicate && soleParam !== undefined && predicateToken !== undefined) {
|
|
5297
6220
|
return `${name}(${params}): ${safeParamName(soleParam.name, 0)} is ${mapType(predicateToken)};`;
|
|
5298
6221
|
}
|
|
5299
|
-
return `${name}(${params}): ${ret.type};${ret.trailing}`;
|
|
6222
|
+
return `${name}${box2dBackendTypeParameters(prepared.original)}(${params}): ${ret.type};${ret.trailing}`;
|
|
5300
6223
|
}
|
|
5301
6224
|
|
|
5302
6225
|
function emitFunction(
|
|
@@ -5367,6 +6290,7 @@ function functionDocLines(
|
|
|
5367
6290
|
};
|
|
5368
6291
|
});
|
|
5369
6292
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
6293
|
+
const sparseNote = SPARSE_ARRAY_RETURNS.get(fn.name)?.note;
|
|
5370
6294
|
const notedReturns =
|
|
5371
6295
|
multiReturnDoc(fn, translations, baseNotes) ?? overrideReturnDoc(fn, baseNotes);
|
|
5372
6296
|
const lua = htmlToCodeText(fn.examples ?? "");
|
|
@@ -5399,7 +6323,10 @@ function functionDocLines(
|
|
|
5399
6323
|
? {
|
|
5400
6324
|
returns: appendNotes(
|
|
5401
6325
|
translateProseFences(htmlToDocText(onlyReturn.doc), fn.name, translations),
|
|
5402
|
-
|
|
6326
|
+
[
|
|
6327
|
+
...baseNotes(fn.name, "return", onlyReturn.name),
|
|
6328
|
+
...(sparseNote === undefined ? [] : [sparseNote]),
|
|
6329
|
+
],
|
|
5403
6330
|
),
|
|
5404
6331
|
}
|
|
5405
6332
|
: notedReturns !== undefined
|
|
@@ -5521,6 +6448,7 @@ function parameterType(
|
|
|
5521
6448
|
constantTokens: ConstantSlotTokens,
|
|
5522
6449
|
elementName: string,
|
|
5523
6450
|
urlParameters: UrlParameterTable,
|
|
6451
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5524
6452
|
): string {
|
|
5525
6453
|
const concrete = p.types.filter((t) => t !== "nil");
|
|
5526
6454
|
// The caller supplies a parameter, so a string-keyed table in it also takes an
|
|
@@ -5542,10 +6470,19 @@ function parameterType(
|
|
|
5542
6470
|
elementName,
|
|
5543
6471
|
"param",
|
|
5544
6472
|
p.name,
|
|
6473
|
+
bindingEvidence,
|
|
5545
6474
|
)
|
|
5546
6475
|
: "unknown";
|
|
5547
|
-
const
|
|
5548
|
-
|
|
6476
|
+
const key = tableSlotKey(elementName, "param", p.name);
|
|
6477
|
+
const correction = PARAM_TYPE_CORRECTIONS.get(key);
|
|
6478
|
+
if (
|
|
6479
|
+
correction === undefined ||
|
|
6480
|
+
!pinMatches(p.types, correctionPins(correction)) ||
|
|
6481
|
+
(correction.binding !== undefined && !bindingEvidence.has(key))
|
|
6482
|
+
) {
|
|
6483
|
+
return mapped;
|
|
6484
|
+
}
|
|
6485
|
+
if (correction.ts !== undefined) return correction.ts;
|
|
5549
6486
|
const kept =
|
|
5550
6487
|
correction.removes === undefined ? mapped : withoutUnionMembers(mapped, correction.removes);
|
|
5551
6488
|
if (correction.adds === undefined) return kept;
|
|
@@ -5627,13 +6564,22 @@ function emitRestParameter(
|
|
|
5627
6564
|
constantTokens: ConstantSlotTokens,
|
|
5628
6565
|
elementName: string,
|
|
5629
6566
|
urlParameters: UrlParameterTable,
|
|
6567
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5630
6568
|
): { text: string; slots: EmittedSlot[] } {
|
|
5631
6569
|
const vararg = params[varargIndex];
|
|
5632
6570
|
if (vararg === undefined) return { text: "", slots: [] };
|
|
5633
6571
|
const folded = params.slice(varargIndex).map((p, offset) => ({
|
|
5634
6572
|
position: varargIndex + offset,
|
|
5635
6573
|
name: p.name,
|
|
5636
|
-
ts: parameterType(
|
|
6574
|
+
ts: parameterType(
|
|
6575
|
+
p,
|
|
6576
|
+
mapType,
|
|
6577
|
+
resolver,
|
|
6578
|
+
constantTokens,
|
|
6579
|
+
elementName,
|
|
6580
|
+
urlParameters,
|
|
6581
|
+
bindingEvidence,
|
|
6582
|
+
),
|
|
5637
6583
|
}));
|
|
5638
6584
|
const members = [...new Set(folded.map((slot) => slot.ts))];
|
|
5639
6585
|
const first = members[0] ?? "unknown";
|
|
@@ -5661,15 +6607,28 @@ function emitParameter(
|
|
|
5661
6607
|
constantTokens: ConstantSlotTokens,
|
|
5662
6608
|
elementName: string,
|
|
5663
6609
|
urlParameters: UrlParameterTable,
|
|
6610
|
+
bindingEvidence: ReadonlySet<string>,
|
|
6611
|
+
required = false,
|
|
6612
|
+
retype: (type: string) => string = (type) => type,
|
|
5664
6613
|
): { text: string; type: string; optional: boolean } {
|
|
5665
6614
|
const name = safeParamName(p.name, index);
|
|
5666
|
-
const ts =
|
|
6615
|
+
const ts = retype(
|
|
6616
|
+
parameterType(
|
|
6617
|
+
p,
|
|
6618
|
+
mapType,
|
|
6619
|
+
resolver,
|
|
6620
|
+
constantTokens,
|
|
6621
|
+
elementName,
|
|
6622
|
+
urlParameters,
|
|
6623
|
+
bindingEvidence,
|
|
6624
|
+
),
|
|
6625
|
+
);
|
|
5667
6626
|
// An interior doc-optional param (a required param follows, so the trailing-`?`
|
|
5668
6627
|
// projection cannot mark it) keeps its optionality as `| undefined` — TSTL
|
|
5669
6628
|
// lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
|
|
5670
6629
|
// `?` form; required params are untouched.
|
|
5671
6630
|
const nilRequired = requiredSlotCorrection(p, elementName)?.nil === true;
|
|
5672
|
-
const interiorOptional = !optional && (isDocOptional(p, elementName) || nilRequired);
|
|
6631
|
+
const interiorOptional = !optional && !required && (isDocOptional(p, elementName) || nilRequired);
|
|
5673
6632
|
// `(a) => void | undefined` reads as a function returning `void | undefined`.
|
|
5674
6633
|
const operand = interiorOptional && isFunctionTypeText(ts) ? `(${ts})` : ts;
|
|
5675
6634
|
const type = `${operand}${interiorOptional ? " | undefined" : ""}`;
|
|
@@ -5734,6 +6693,7 @@ function emitReturn(
|
|
|
5734
6693
|
elementName,
|
|
5735
6694
|
"return",
|
|
5736
6695
|
rv.name,
|
|
6696
|
+
bindingEvidence,
|
|
5737
6697
|
)
|
|
5738
6698
|
: "unknown"),
|
|
5739
6699
|
};
|
|
@@ -5772,6 +6732,7 @@ function emitReturn(
|
|
|
5772
6732
|
elementName,
|
|
5773
6733
|
"return",
|
|
5774
6734
|
first.name,
|
|
6735
|
+
bindingEvidence,
|
|
5775
6736
|
)
|
|
5776
6737
|
: "unknown";
|
|
5777
6738
|
// A LuaLS vararg return (`...`) is every value the call returns, however many.
|
|
@@ -5811,12 +6772,17 @@ function emitPropertyMembers(
|
|
|
5811
6772
|
p: ApiProperty,
|
|
5812
6773
|
mapType: DefoldTypeMapper,
|
|
5813
6774
|
namespace: string,
|
|
6775
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5814
6776
|
): readonly string[] {
|
|
5815
|
-
const
|
|
6777
|
+
const rangeKey = `${namespace}.${p.name}`;
|
|
6778
|
+
const keyRange = PROPERTY_KEY_RANGES.get(rangeKey);
|
|
5816
6779
|
const keys: string[] = [];
|
|
5817
6780
|
if (keyRange !== undefined) {
|
|
5818
|
-
|
|
5819
|
-
|
|
6781
|
+
const last =
|
|
6782
|
+
keyRange.engine !== undefined && bindingEvidence.has(rangeKey)
|
|
6783
|
+
? keyRange.engine.last
|
|
6784
|
+
: keyRange.last;
|
|
6785
|
+
for (let unit = keyRange.first; unit <= last; unit++) keys.push(`${keyRange.prefix}${unit}`);
|
|
5820
6786
|
} else {
|
|
5821
6787
|
keys.push(TS_IDENTIFIER.test(p.name) ? p.name : JSON.stringify(p.name));
|
|
5822
6788
|
}
|
|
@@ -5838,6 +6804,48 @@ function emitPropertyMembers(
|
|
|
5838
6804
|
return keys.map((key) => `${modifier}${key}: ${ts};`);
|
|
5839
6805
|
}
|
|
5840
6806
|
|
|
6807
|
+
// A function whose returned table the engine binding keys by a sparse integer,
|
|
6808
|
+
// so the sequence its reference declares is a map with holes: `ipairs` stops at
|
|
6809
|
+
// the first hole, and `#t` may return any border. Keyed by function name, since the return slot is named
|
|
6810
|
+
// `table` in 1.13.0/1.13.1 and `textures` from 1.13.2. The return's `T[]` member
|
|
6811
|
+
// emits as `LuaMap<number, T>`, which `for...of` lowers to `pairs`, and `note`
|
|
6812
|
+
// joins its `@returns`.
|
|
6813
|
+
export const SPARSE_ARRAY_RETURNS: ReadonlyMap<string, { note: string; reason: string }> = new Map([
|
|
6814
|
+
[
|
|
6815
|
+
"material.get_textures",
|
|
6816
|
+
{
|
|
6817
|
+
note: "Keyed by sampler slot from 1; a slot with no texture has no entry, so iterate with `for (const [slot, info] of textures)`.",
|
|
6818
|
+
reason:
|
|
6819
|
+
"script_material.cpp:Material_GetTextures pushes key i+1 only for a slot that has a texture, unlike Compute_GetTextures, which packs its keys",
|
|
6820
|
+
},
|
|
6821
|
+
],
|
|
6822
|
+
]);
|
|
6823
|
+
|
|
6824
|
+
function sparseArrayMap(ts: string, elementName: string): string {
|
|
6825
|
+
const members = unionMembers(ts);
|
|
6826
|
+
const arrays = members.filter((member) => member.endsWith("[]"));
|
|
6827
|
+
if (arrays.length === 0) {
|
|
6828
|
+
throw new Error(`SPARSE_ARRAY_RETURNS ${elementName}: the return emits no array (${ts})`);
|
|
6829
|
+
}
|
|
6830
|
+
return members
|
|
6831
|
+
.map((member) => {
|
|
6832
|
+
if (!member.endsWith("[]")) return member;
|
|
6833
|
+
const element = member.slice(0, -2);
|
|
6834
|
+
return `LuaMap<number, ${wrappedInParens(element) ? element.slice(1, -1) : element}>`;
|
|
6835
|
+
})
|
|
6836
|
+
.join(" | ");
|
|
6837
|
+
}
|
|
6838
|
+
|
|
6839
|
+
function wrappedInParens(text: string): boolean {
|
|
6840
|
+
if (!text.startsWith("(")) return false;
|
|
6841
|
+
let depth = 0;
|
|
6842
|
+
for (let i = 0; i < text.length; i++) {
|
|
6843
|
+
if (text[i] === "(") depth++;
|
|
6844
|
+
else if (text[i] === ")" && --depth === 0) return i === text.length - 1;
|
|
6845
|
+
}
|
|
6846
|
+
return false;
|
|
6847
|
+
}
|
|
6848
|
+
|
|
5841
6849
|
// Like `unionFromTokens`, but a `table` token whose slot doc carries a parseable
|
|
5842
6850
|
// `<dl>` field list emits an inline object type instead of the opaque `Record`
|
|
5843
6851
|
// fallback. Other tokens in the union map unchanged. `optionalFields` marks the
|
|
@@ -5853,8 +6861,9 @@ function mapSlotUnion(
|
|
|
5853
6861
|
resolver: TableDocResolver,
|
|
5854
6862
|
constantTokens: ConstantSlotTokens,
|
|
5855
6863
|
elementName: string,
|
|
5856
|
-
slotKind
|
|
5857
|
-
slotName
|
|
6864
|
+
slotKind: "param" | "return" | undefined,
|
|
6865
|
+
slotName: string | undefined,
|
|
6866
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5858
6867
|
): string {
|
|
5859
6868
|
const mapped: string[] = [];
|
|
5860
6869
|
const seen = new Set<string>();
|
|
@@ -5918,6 +6927,10 @@ function mapSlotUnion(
|
|
|
5918
6927
|
}
|
|
5919
6928
|
} else if (element !== undefined) {
|
|
5920
6929
|
ts = arrayTypeFromTokens(element, mapType);
|
|
6930
|
+
if (curation?.kind === "array" && curation.pairs === true) {
|
|
6931
|
+
const tokens = typeof element === "string" ? [element] : element;
|
|
6932
|
+
ts = `${ts} | LuaPairsIterable<AnyNotNil, ${unionFromTokens(tokens, mapType)}>`;
|
|
6933
|
+
}
|
|
5921
6934
|
} else if (curation?.kind === "verbatim") {
|
|
5922
6935
|
ts = curation.ts;
|
|
5923
6936
|
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|
|
@@ -5943,6 +6956,7 @@ function mapSlotUnion(
|
|
|
5943
6956
|
slotKind,
|
|
5944
6957
|
slotName,
|
|
5945
6958
|
parseTableFields(doc, resolver, slotName) ?? [],
|
|
6959
|
+
bindingEvidence,
|
|
5946
6960
|
),
|
|
5947
6961
|
);
|
|
5948
6962
|
const object = inlineTableType(parsed, mapType, optionalFields);
|
|
@@ -5952,7 +6966,13 @@ function mapSlotUnion(
|
|
|
5952
6966
|
if (parsed !== null) {
|
|
5953
6967
|
const added = applyFieldAdditions(elementName, slotKind, slotName, parsed);
|
|
5954
6968
|
const nested = applyNestedFieldCurations(elementName, slotKind, slotName, added);
|
|
5955
|
-
const typed = applyFieldTypeOverrides(
|
|
6969
|
+
const typed = applyFieldTypeOverrides(
|
|
6970
|
+
elementName,
|
|
6971
|
+
slotKind,
|
|
6972
|
+
slotName,
|
|
6973
|
+
nested,
|
|
6974
|
+
bindingEvidence,
|
|
6975
|
+
);
|
|
5956
6976
|
const required = applyRequiredFieldCorrections(
|
|
5957
6977
|
elementName,
|
|
5958
6978
|
slotKind,
|
|
@@ -5991,8 +7011,10 @@ function mapSlotUnion(
|
|
|
5991
7011
|
// A genuine `unknown` member (an `any` slot, including `any | nil`) absorbs
|
|
5992
7012
|
// every other union member, so collapse to exactly `unknown` rather than emit
|
|
5993
7013
|
// a redundant `T | unknown` or `unknown | undefined`.
|
|
5994
|
-
|
|
5995
|
-
return
|
|
7014
|
+
const joined = mapped.includes("unknown") ? "unknown" : joinUnionArms(mapped);
|
|
7015
|
+
return slotKind === "return" && SPARSE_ARRAY_RETURNS.has(elementName)
|
|
7016
|
+
? sparseArrayMap(joined, elementName)
|
|
7017
|
+
: joined;
|
|
5996
7018
|
}
|
|
5997
7019
|
|
|
5998
7020
|
function tableSlotKey(elementName: string, slotKind: "param" | "return", slotName: string): string {
|
|
@@ -6120,8 +7142,8 @@ function withoutSubsumedOpaqueArms(mapped: readonly string[]): string[] {
|
|
|
6120
7142
|
});
|
|
6121
7143
|
}
|
|
6122
7144
|
|
|
6123
|
-
// Joins already-mapped arms, collapsing the branded ones to their
|
|
6124
|
-
// they are exactly a `CONSTANT_UNION_ALIASES` member set. The
|
|
7145
|
+
// Joins already-mapped arms, collapsing the branded ones to their row's spelling
|
|
7146
|
+
// when they are exactly a `CONSTANT_UNION_ALIASES` member set. The spelling takes the
|
|
6125
7147
|
// position of the first branded arm, so a mixed union keeps its reading order
|
|
6126
7148
|
// (`gui.Easing | Vector`). Arms that are not constants are untouched, and a set
|
|
6127
7149
|
// with no row keeps every arm inline.
|
|
@@ -6139,7 +7161,7 @@ function joinUnionArms(mapped: readonly string[]): string {
|
|
|
6139
7161
|
for (const arm of arms) {
|
|
6140
7162
|
if (BRAND_ARM.test(arm)) {
|
|
6141
7163
|
if (placed) continue;
|
|
6142
|
-
out.push(
|
|
7164
|
+
out.push(constantUnionSpelling(alias));
|
|
6143
7165
|
placed = true;
|
|
6144
7166
|
continue;
|
|
6145
7167
|
}
|