@defold-typescript/types 0.5.4 → 0.6.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-targets.json +1 -1
- package/generated/b2d.d.ts +3 -0
- package/generated/buffer.d.ts +44 -38
- package/generated/builtin-messages.d.ts +1 -1
- package/generated/camera.d.ts +3 -0
- package/generated/collectionfactory.d.ts +47 -40
- package/generated/collectionproxy.d.ts +23 -18
- package/generated/crash.d.ts +3 -0
- package/generated/factory.d.ts +32 -24
- package/generated/go.d.ts +293 -293
- package/generated/graphics.d.ts +3 -0
- package/generated/gui.d.ts +303 -283
- package/generated/http.d.ts +26 -16
- package/generated/iac.d.ts +3 -0
- package/generated/iap.d.ts +6 -3
- package/generated/image.d.ts +30 -26
- package/generated/json.d.ts +36 -32
- package/generated/kinds/gui-script.d.ts +7 -5
- package/generated/kinds/render-script.d.ts +7 -5
- package/generated/kinds/script.d.ts +7 -5
- package/generated/label.d.ts +16 -9
- package/generated/liveupdate.d.ts +29 -26
- package/generated/model.d.ts +57 -45
- package/generated/msg.d.ts +12 -9
- package/generated/particlefx.d.ts +50 -34
- package/generated/physics.d.ts +153 -133
- package/generated/profiler.d.ts +45 -41
- package/generated/push.d.ts +5 -2
- package/generated/render.d.ts +410 -349
- package/generated/resource.d.ts +619 -572
- package/generated/socket.d.ts +49 -33
- package/generated/sound.d.ts +83 -72
- package/generated/sprite.d.ts +36 -32
- package/generated/sys.d.ts +198 -189
- package/generated/tilemap.d.ts +43 -39
- package/generated/timer.d.ts +42 -36
- package/generated/vmath.d.ts +254 -229
- package/generated/webview.d.ts +3 -0
- package/generated/window.d.ts +23 -17
- package/generated/zlib.d.ts +15 -12
- package/index.d.ts +3 -1
- package/package.json +6 -2
- package/scripts/example-store-io.ts +18 -0
- package/scripts/fidelity-audit.ts +61 -1
- package/scripts/fidelity-baseline.json +10 -10
- package/scripts/ref-doc-delta.ts +143 -0
- package/scripts/regen.ts +23 -10
- package/src/core-types.ts +14 -0
- package/src/doc-comment.ts +2 -1
- package/src/emit-dts.ts +238 -18
- package/src/engine-globals.d.ts +2 -0
- package/src/example-store.ts +44 -0
- package/src/go-overloads.d.ts +73 -0
- package/src/index.ts +5 -0
- package/src/lifecycle.ts +157 -16
- package/src/message-dispatch.d.ts +21 -0
- package/src/message-guard.d.ts +19 -0
- package/src/msg-overloads.d.ts +20 -0
- package/src/publish-dts.ts +1 -1
package/src/emit-dts.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
htmlToDocText,
|
|
14
14
|
renderDocComment,
|
|
15
15
|
} from "./doc-comment";
|
|
16
|
+
import type { TranslationStore } from "./example-store";
|
|
17
|
+
import { hashExampleSource, lookupTranslation } from "./example-store";
|
|
16
18
|
|
|
17
19
|
export interface EmitOptions {
|
|
18
20
|
mapType?: (defoldType: string) => string;
|
|
@@ -21,6 +23,12 @@ export interface EmitOptions {
|
|
|
21
23
|
// brands to the same FQN-keyed type its owning module's `const` emits,
|
|
22
24
|
// instead of widening to `unknown`.
|
|
23
25
|
knownConstantFqns?: ReadonlySet<string>;
|
|
26
|
+
// Hand-authored TypeScript `@example` translations keyed by element FQN.
|
|
27
|
+
// Defaults to an empty store (every example stays on its Lua fallback,
|
|
28
|
+
// byte-identical output); the build layer (`regen`) passes the loaded
|
|
29
|
+
// `examples/translations.json`. Loading lives in `scripts/example-store-io.ts`
|
|
30
|
+
// so this module stays node-free for downstream consumers.
|
|
31
|
+
translations?: TranslationStore;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
export const TS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
@@ -74,8 +82,11 @@ export const TS_RESERVED_NAMES = new Set([
|
|
|
74
82
|
"extends",
|
|
75
83
|
]);
|
|
76
84
|
|
|
77
|
-
// Element names whose `table` slot is a genuinely-arbitrary lua table by design
|
|
78
|
-
//
|
|
85
|
+
// Element names whose `table` slot is a genuinely-arbitrary lua table by design.
|
|
86
|
+
// Two kinds: the serialization/JSON passthrough functions (Defold-internal — the
|
|
87
|
+
// engine round-trips an arbitrary lua value), and the platform/OS-sourced opaque
|
|
88
|
+
// blobs (external — the shape is set by the host OS or invoking app, not by
|
|
89
|
+
// Defold, so there is no documented field list). Their emitted
|
|
79
90
|
// `Record<string | number, unknown>` is the faithful "any lua table" type, not a
|
|
80
91
|
// `recordTables` fidelity loss, so the audit consults this set to avoid counting
|
|
81
92
|
// them. A new ref-doc function with an opaque table must be added here
|
|
@@ -87,6 +98,9 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
87
98
|
"sys.load",
|
|
88
99
|
"sys.serialize",
|
|
89
100
|
"sys.deserialize",
|
|
101
|
+
"iac.set_listener",
|
|
102
|
+
"push.get_scheduled",
|
|
103
|
+
"push.get_all_scheduled",
|
|
90
104
|
]);
|
|
91
105
|
|
|
92
106
|
// Element names whose `table` slot is a prose-only `a table mapping X to Y`
|
|
@@ -123,6 +137,157 @@ export const HOMOGENEOUS_ARRAY_SLOTS: ReadonlyMap<string, string | readonly stri
|
|
|
123
137
|
["sound.get_groups", "hash"],
|
|
124
138
|
["iap.list", "string"],
|
|
125
139
|
["go.delete", ["string", "hash", "url"]],
|
|
140
|
+
// push.register's `notifications` is a prose-only array of push.NOTIFICATION_*
|
|
141
|
+
// bitmask constants (the ref example sums NOTIFICATION_BADGE/SOUND/ALERT). The
|
|
142
|
+
// vendored push_doc.json fixture declares no NOTIFICATION_* constant elements, so
|
|
143
|
+
// no brand exists to reference; `number` is the faithful element token for these
|
|
144
|
+
// numeric constants, mirroring the vmath.vector/buffer.* number entries.
|
|
145
|
+
["push.register", "number"],
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
// A `mapping` curation whose value is itself a single-level mapping, emitted
|
|
149
|
+
// `LuaMap<Kouter, LuaMap<Kinner, Vinner>>` — the nested-row-map shape
|
|
150
|
+
// (`tilemap.get_tiles` → `tiles[row][col]`).
|
|
151
|
+
export type NestedMapping = { key: string; value: string };
|
|
152
|
+
|
|
153
|
+
export type TableSlotCuration =
|
|
154
|
+
| { kind: "mapping"; key: string; value: string | readonly TableField[] | NestedMapping }
|
|
155
|
+
| { kind: "array"; element: string | readonly string[] }
|
|
156
|
+
| { kind: "object"; fields: readonly TableField[] }
|
|
157
|
+
| { kind: "array-object"; fields: readonly TableField[] };
|
|
158
|
+
|
|
159
|
+
const SOCKET_HANDLE_TOKENS = ["client", "master", "unconnected"] as const;
|
|
160
|
+
|
|
161
|
+
export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new Map([
|
|
162
|
+
["collectionfactory.create:return:ids", { kind: "mapping", key: "hash", value: "hash" }],
|
|
163
|
+
// iap.finish and iap.acknowledge take the same Defold IAP transaction object —
|
|
164
|
+
// the table handed to the iap.set_listener callback. The ref-doc fixture
|
|
165
|
+
// describes it in prose only (no field list), so the shape is curated from the
|
|
166
|
+
// Defold iap reference. As a param-side object curation every field emits `?`,
|
|
167
|
+
// which is faithful: original_trans/signature/user_id are platform-specific.
|
|
168
|
+
[
|
|
169
|
+
"iap.finish:param:transaction",
|
|
170
|
+
{
|
|
171
|
+
kind: "object",
|
|
172
|
+
fields: [
|
|
173
|
+
{ name: "ident", types: ["string"] },
|
|
174
|
+
{ name: "state", types: ["number"] },
|
|
175
|
+
{ name: "trans_ident", types: ["string"] },
|
|
176
|
+
{ name: "date", types: ["string"] },
|
|
177
|
+
{ name: "original_trans", types: ["string"] },
|
|
178
|
+
{ name: "receipt", types: ["string"] },
|
|
179
|
+
{ name: "signature", types: ["string"] },
|
|
180
|
+
{ name: "user_id", types: ["string"] },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
"iap.acknowledge:param:transaction",
|
|
186
|
+
{
|
|
187
|
+
kind: "object",
|
|
188
|
+
fields: [
|
|
189
|
+
{ name: "ident", types: ["string"] },
|
|
190
|
+
{ name: "state", types: ["number"] },
|
|
191
|
+
{ name: "trans_ident", types: ["string"] },
|
|
192
|
+
{ name: "date", types: ["string"] },
|
|
193
|
+
{ name: "original_trans", types: ["string"] },
|
|
194
|
+
{ name: "receipt", types: ["string"] },
|
|
195
|
+
{ name: "signature", types: ["string"] },
|
|
196
|
+
{ name: "user_id", types: ["string"] },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
// iap.buy's `options` is documented in the fixture as prose only ("optional
|
|
201
|
+
// parameters as properties"), so the field set is curated from the Defold iap
|
|
202
|
+
// reference: a Facebook-only custom `request_id` and a Google-Play-only
|
|
203
|
+
// subscription-offer `token`. Both are platform-specific, hence param-side
|
|
204
|
+
// optional fields.
|
|
205
|
+
[
|
|
206
|
+
"iap.buy:param:options",
|
|
207
|
+
{
|
|
208
|
+
kind: "object",
|
|
209
|
+
fields: [
|
|
210
|
+
{ name: "request_id", types: ["string"] },
|
|
211
|
+
{ name: "token", types: ["string"] },
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
[
|
|
216
|
+
"liveupdate.get_mounts:return:mounts",
|
|
217
|
+
{
|
|
218
|
+
kind: "array-object",
|
|
219
|
+
fields: [
|
|
220
|
+
{ name: "name", types: ["string"] },
|
|
221
|
+
{ name: "uri", types: ["string"] },
|
|
222
|
+
{ name: "priority", types: ["number"] },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
"model.get_aabb:return:aabb",
|
|
228
|
+
{
|
|
229
|
+
kind: "object",
|
|
230
|
+
fields: [
|
|
231
|
+
{ name: "min", types: ["vector3"] },
|
|
232
|
+
{ name: "max", types: ["vector3"] },
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
[
|
|
237
|
+
"model.get_mesh_aabb:return:aabb",
|
|
238
|
+
{
|
|
239
|
+
kind: "mapping",
|
|
240
|
+
key: "hash",
|
|
241
|
+
value: [
|
|
242
|
+
{ name: "min", types: ["vector3"] },
|
|
243
|
+
{ name: "max", types: ["vector3"] },
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
["physics.raycast:param:groups", { kind: "array", element: "hash" }],
|
|
248
|
+
["physics.raycast_async:param:groups", { kind: "array", element: "hash" }],
|
|
249
|
+
// push.schedule's `notification_settings` is documented in the fixture as prose
|
|
250
|
+
// only ("Table with notification and platform specific fields"), so the field
|
|
251
|
+
// set is curated from the Defold push reference: an iOS `action`, an iOS
|
|
252
|
+
// `badge_count`, and an Android `priority`. Each is platform-specific, hence
|
|
253
|
+
// param-side optional fields.
|
|
254
|
+
[
|
|
255
|
+
"push.schedule:param:notification_settings",
|
|
256
|
+
{
|
|
257
|
+
kind: "object",
|
|
258
|
+
fields: [
|
|
259
|
+
{ name: "action", types: ["string"] },
|
|
260
|
+
{ name: "badge_count", types: ["number"] },
|
|
261
|
+
{ name: "priority", types: ["number"] },
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
["socket.select:param:recvt", { kind: "array", element: SOCKET_HANDLE_TOKENS }],
|
|
266
|
+
["socket.select:param:sendt", { kind: "array", element: SOCKET_HANDLE_TOKENS }],
|
|
267
|
+
["socket.select:return:sockets_r", { kind: "array", element: SOCKET_HANDLE_TOKENS }],
|
|
268
|
+
["socket.select:return:sockets_w", { kind: "array", element: SOCKET_HANDLE_TOKENS }],
|
|
269
|
+
[
|
|
270
|
+
"tilemap.get_tile_info:return:tile_info",
|
|
271
|
+
{
|
|
272
|
+
kind: "object",
|
|
273
|
+
fields: [
|
|
274
|
+
{ name: "index", types: ["number"] },
|
|
275
|
+
{ name: "h_flip", types: ["boolean"] },
|
|
276
|
+
{ name: "v_flip", types: ["boolean"] },
|
|
277
|
+
{ name: "rotate_90", types: ["boolean"] },
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
// tilemap.get_tiles returns a sparse table of rows iterated `tiles[row][col]`,
|
|
282
|
+
// the keys being tile positions offset by tilemap.get_bounds() (not a dense
|
|
283
|
+
// 0..n run). The faithful shape is the nested LuaMap idiom
|
|
284
|
+
// `LuaMap<number, LuaMap<number, number>>` — the keys are plain `number` tile
|
|
285
|
+
// positions, not a branded `Hash`, and a non-string-keyed Lua table is `LuaMap`,
|
|
286
|
+
// never a `Record` (which would imply a dense index object).
|
|
287
|
+
[
|
|
288
|
+
"tilemap.get_tiles:return:tiles",
|
|
289
|
+
{ kind: "mapping", key: "number", value: { key: "number", value: "number" } },
|
|
290
|
+
],
|
|
126
291
|
]);
|
|
127
292
|
|
|
128
293
|
/**
|
|
@@ -427,6 +592,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
427
592
|
|
|
428
593
|
const constantFqns = new Set(module.constants.map((c) => c.name));
|
|
429
594
|
const knownConstantFqns = options?.knownConstantFqns;
|
|
595
|
+
const translations = options?.translations ?? {};
|
|
430
596
|
const baseMapType = options?.mapType ?? defaultMapType;
|
|
431
597
|
const mapType = (token: string): string =>
|
|
432
598
|
constantFqns.has(token) || knownConstantFqns?.has(token)
|
|
@@ -473,6 +639,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
473
639
|
const decl = hasAliases ? "export " : "";
|
|
474
640
|
|
|
475
641
|
const lines: string[] = [];
|
|
642
|
+
for (const docLine of namespaceDocLines(module)) lines.push(docLine);
|
|
476
643
|
lines.push(`declare namespace ${module.namespace} {`);
|
|
477
644
|
|
|
478
645
|
for (const t of typedefs) {
|
|
@@ -502,7 +669,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
|
|
|
502
669
|
for (const fn of functions) {
|
|
503
670
|
const reserved = TS_RESERVED_NAMES.has(fn.name);
|
|
504
671
|
const emitName = aliasName(fn.name, aliases);
|
|
505
|
-
for (const docLine of functionDocLines(fn.original)) lines.push(docLine);
|
|
672
|
+
for (const docLine of functionDocLines(fn.original, translations)) lines.push(docLine);
|
|
506
673
|
const line = emitFunction(fn, emitName, mapType, resolver);
|
|
507
674
|
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
508
675
|
}
|
|
@@ -614,18 +781,23 @@ function emitFunction(
|
|
|
614
781
|
// fallback applies to non-identifier names, matching `emitParameter`) so the tag
|
|
615
782
|
// resolves on hover; a single documented return becomes `@returns`. Returns `[]`
|
|
616
783
|
// for a fully-undocumented function, leaving its emission byte-identical.
|
|
617
|
-
function functionDocLines(fn: ApiFunction): string[] {
|
|
784
|
+
function functionDocLines(fn: ApiFunction, translations: TranslationStore): string[] {
|
|
618
785
|
const params = fn.parameters.map((p, index) => ({
|
|
619
786
|
name: TS_IDENTIFIER.test(p.name) ? p.name : `arg${index}`,
|
|
620
787
|
doc: htmlToDocText(p.doc),
|
|
621
788
|
}));
|
|
622
789
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
623
|
-
const
|
|
790
|
+
const lua = htmlToCodeText(fn.examples ?? "");
|
|
791
|
+
// A hand-authored TS translation pinned to this exact Lua flips the fence to
|
|
792
|
+
// ```ts; any hash mismatch (or absent translation) keeps the Lua fallback.
|
|
793
|
+
const ts = lua === "" ? null : lookupTranslation(translations, fn.name, hashExampleSource(lua));
|
|
794
|
+
const exampleParts: Pick<DocCommentParts, "example" | "exampleLang"> =
|
|
795
|
+
ts !== null ? { example: ts, exampleLang: "ts" } : lua !== "" ? { example: lua } : {};
|
|
624
796
|
const parts: DocCommentParts = {
|
|
625
797
|
summary: htmlToDocText(summaryFor(fn.brief, fn.description)),
|
|
626
798
|
params,
|
|
627
799
|
...(onlyReturn ? { returns: htmlToDocText(onlyReturn.doc) } : {}),
|
|
628
|
-
...
|
|
800
|
+
...exampleParts,
|
|
629
801
|
};
|
|
630
802
|
return indentDocLines(parts, INDENT);
|
|
631
803
|
}
|
|
@@ -639,6 +811,15 @@ function summaryDocLines(brief: string, description: string, indent: string): st
|
|
|
639
811
|
return indentDocLines({ summary: htmlToDocText(summaryFor(brief, description)) }, indent);
|
|
640
812
|
}
|
|
641
813
|
|
|
814
|
+
function namespaceDocLines(module: ApiModule): string[] {
|
|
815
|
+
const official = summaryFor(module.brief, module.description).trim();
|
|
816
|
+
const summary =
|
|
817
|
+
official.length > 0
|
|
818
|
+
? htmlToDocText(official)
|
|
819
|
+
: `(synthesized)\nDefold \`${module.namespace}\` API namespace.`;
|
|
820
|
+
return indentDocLines({ summary }, "");
|
|
821
|
+
}
|
|
822
|
+
|
|
642
823
|
function indentDocLines(parts: DocCommentParts, indent: string): string[] {
|
|
643
824
|
return renderDocComment(parts).map((line) => `${indent}${line}`);
|
|
644
825
|
}
|
|
@@ -646,7 +827,7 @@ function indentDocLines(parts: DocCommentParts, indent: string): string[] {
|
|
|
646
827
|
// Prefer the full `description`; fall back to the one-line `brief` when prose is
|
|
647
828
|
// absent. Shared by every documented member kind so the summary source is
|
|
648
829
|
// consistent across functions, constants, variables, and properties.
|
|
649
|
-
function summaryFor(brief: string, description: string): string {
|
|
830
|
+
export function summaryFor(brief: string, description: string): string {
|
|
650
831
|
return description.trim() !== "" ? description : brief;
|
|
651
832
|
}
|
|
652
833
|
|
|
@@ -676,7 +857,7 @@ function emitParameter(
|
|
|
676
857
|
const concrete = p.types.filter((t) => t !== "nil");
|
|
677
858
|
const ts =
|
|
678
859
|
concrete.length > 0
|
|
679
|
-
? mapSlotUnion(concrete, p.doc, mapType, true, resolver, elementName)
|
|
860
|
+
? mapSlotUnion(concrete, p.doc, mapType, true, resolver, elementName, "param", p.name)
|
|
680
861
|
: "unknown";
|
|
681
862
|
return `${name}${optional ? "?" : ""}: ${ts}`;
|
|
682
863
|
}
|
|
@@ -694,7 +875,7 @@ function emitReturn(
|
|
|
694
875
|
// typescript-to-lua erases to `local a, b = fn()`.
|
|
695
876
|
const slots = returnValues.map((rv) =>
|
|
696
877
|
rv.types.length > 0
|
|
697
|
-
? mapSlotUnion(rv.types, rv.doc, mapType, false, resolver, elementName)
|
|
878
|
+
? mapSlotUnion(rv.types, rv.doc, mapType, false, resolver, elementName, "return", rv.name)
|
|
698
879
|
: "unknown",
|
|
699
880
|
);
|
|
700
881
|
return { type: `LuaMultiReturn<[${slots.join(", ")}]>`, trailing: "" };
|
|
@@ -703,7 +884,16 @@ function emitReturn(
|
|
|
703
884
|
if (!first) return { type: "void", trailing: "" };
|
|
704
885
|
const ts =
|
|
705
886
|
first.types.length > 0
|
|
706
|
-
? mapSlotUnion(
|
|
887
|
+
? mapSlotUnion(
|
|
888
|
+
first.types,
|
|
889
|
+
first.doc,
|
|
890
|
+
mapType,
|
|
891
|
+
false,
|
|
892
|
+
resolver,
|
|
893
|
+
elementName,
|
|
894
|
+
"return",
|
|
895
|
+
first.name,
|
|
896
|
+
)
|
|
707
897
|
: "unknown";
|
|
708
898
|
return { type: ts, trailing: "" };
|
|
709
899
|
}
|
|
@@ -728,22 +918,38 @@ function mapSlotUnion(
|
|
|
728
918
|
optionalFields: boolean,
|
|
729
919
|
resolver: TableDocResolver,
|
|
730
920
|
elementName: string,
|
|
921
|
+
slotKind?: "param" | "return",
|
|
922
|
+
slotName?: string,
|
|
731
923
|
): string {
|
|
732
924
|
const mapped: string[] = [];
|
|
733
925
|
const seen = new Set<string>();
|
|
734
926
|
for (const token of types) {
|
|
735
927
|
let ts: string;
|
|
736
928
|
if (token === "table") {
|
|
737
|
-
const
|
|
738
|
-
|
|
929
|
+
const curation =
|
|
930
|
+
slotKind !== undefined && slotName !== undefined
|
|
931
|
+
? TABLE_SLOT_CURATIONS.get(tableSlotKey(elementName, slotKind, slotName))
|
|
932
|
+
: undefined;
|
|
933
|
+
const mapping =
|
|
934
|
+
curation?.kind === "mapping" ? curation : MAPPING_TABLE_SLOTS.get(elementName);
|
|
935
|
+
const element =
|
|
936
|
+
curation?.kind === "array" ? curation.element : HOMOGENEOUS_ARRAY_SLOTS.get(elementName);
|
|
739
937
|
if (mapping !== undefined) {
|
|
740
|
-
|
|
938
|
+
let value: string;
|
|
939
|
+
if (typeof mapping.value === "string") {
|
|
940
|
+
value = mapType(mapping.value);
|
|
941
|
+
} else if (Array.isArray(mapping.value)) {
|
|
942
|
+
value = inlineTableType(mapping.value, mapType, optionalFields);
|
|
943
|
+
} else {
|
|
944
|
+
const nested = mapping.value as NestedMapping;
|
|
945
|
+
value = `LuaMap<${mapType(nested.key)}, ${mapType(nested.value)}>`;
|
|
946
|
+
}
|
|
947
|
+
ts = `LuaMap<${mapType(mapping.key)}, ${value}>`;
|
|
741
948
|
} else if (element !== undefined) {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
: `${mapType(tokens[0] as string)}[]`;
|
|
949
|
+
ts = arrayTypeFromTokens(element, mapType);
|
|
950
|
+
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|
|
951
|
+
const object = inlineTableType(curation.fields, mapType, optionalFields);
|
|
952
|
+
ts = curation.kind === "array-object" ? `${object}[]` : object;
|
|
747
953
|
} else {
|
|
748
954
|
const fields = parseTableFields(doc, resolver);
|
|
749
955
|
if (fields !== null) {
|
|
@@ -763,6 +969,20 @@ function mapSlotUnion(
|
|
|
763
969
|
return mapped.join(" | ");
|
|
764
970
|
}
|
|
765
971
|
|
|
972
|
+
function tableSlotKey(elementName: string, slotKind: "param" | "return", slotName: string): string {
|
|
973
|
+
return `${elementName}:${slotKind}:${slotName}`;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function arrayTypeFromTokens(
|
|
977
|
+
element: string | readonly string[],
|
|
978
|
+
mapType: (t: string) => string,
|
|
979
|
+
): string {
|
|
980
|
+
const tokens = typeof element === "string" ? [element] : element;
|
|
981
|
+
return tokens.length > 1
|
|
982
|
+
? `(${unionFromTokens(tokens, mapType)})[]`
|
|
983
|
+
: `${mapType(tokens[0] as string)}[]`;
|
|
984
|
+
}
|
|
985
|
+
|
|
766
986
|
export function inlineTableType(
|
|
767
987
|
fields: readonly TableField[],
|
|
768
988
|
mapType: (t: string) => string,
|
package/src/engine-globals.d.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// A hand-authored TypeScript translation of one element's ref-doc `@example`,
|
|
2
|
+
// pinned by a hash of the exact source Lua it replaces. A ref-doc re-pin that
|
|
3
|
+
// changes the source Lua flips the hash, so a stale translation stops matching
|
|
4
|
+
// (drift guard) and the emit falls back to the Lua body.
|
|
5
|
+
export interface Translation {
|
|
6
|
+
sourceHash: string;
|
|
7
|
+
ts: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type TranslationStore = Record<string, Translation>;
|
|
11
|
+
|
|
12
|
+
const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
|
|
13
|
+
const FNV_PRIME = 0x100000001b3n;
|
|
14
|
+
const U64_MASK = 0xffffffffffffffffn;
|
|
15
|
+
|
|
16
|
+
// A pure, dependency-free FNV-1a 64-bit hash over the source's UTF-16 code
|
|
17
|
+
// units, returned as zero-padded hex. Deliberately node- and Bun-free: this
|
|
18
|
+
// module is reachable from `index.ts` (via `emit-dts`), so a `node:crypto` or
|
|
19
|
+
// ambient-`Bun` reference here would fail type-checking in every downstream
|
|
20
|
+
// consumer that compiles the shipped `src/` graph.
|
|
21
|
+
//
|
|
22
|
+
// The input is the already-normalized post-`htmlToCodeText` string (per-line
|
|
23
|
+
// trailing whitespace and surrounding blank lines stripped), so the hash is
|
|
24
|
+
// independent of trailing whitespace in the original ref-doc HTML.
|
|
25
|
+
export function hashExampleSource(source: string): string {
|
|
26
|
+
let hash = FNV_OFFSET_BASIS;
|
|
27
|
+
for (let i = 0; i < source.length; i++) {
|
|
28
|
+
hash = ((hash ^ BigInt(source.charCodeAt(i))) * FNV_PRIME) & U64_MASK;
|
|
29
|
+
}
|
|
30
|
+
return hash.toString(16).padStart(16, "0");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Return the stored TypeScript body only when the FQN exists and its pinned
|
|
34
|
+
// `sourceHash` matches the source we are about to emit; any mismatch returns
|
|
35
|
+
// `null` so the caller keeps the Lua fallback.
|
|
36
|
+
export function lookupTranslation(
|
|
37
|
+
store: TranslationStore,
|
|
38
|
+
fqn: string,
|
|
39
|
+
sourceHash: string,
|
|
40
|
+
): string | null {
|
|
41
|
+
const entry = store[fqn];
|
|
42
|
+
if (!entry || entry.sourceHash !== sourceHash) return null;
|
|
43
|
+
return entry.ts;
|
|
44
|
+
}
|
package/src/go-overloads.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/** @noSelfInFile */
|
|
2
|
+
|
|
2
3
|
import type { Hash, Opaque, Quaternion, Url, Vector3, Vector4 } from "./core-types";
|
|
3
4
|
|
|
5
|
+
interface ScriptProperty<TValue> {
|
|
6
|
+
readonly __defoldScriptProperty: TValue;
|
|
7
|
+
}
|
|
8
|
+
|
|
4
9
|
declare global {
|
|
5
10
|
namespace go {
|
|
6
11
|
interface GoPropertyOptions {
|
|
@@ -9,6 +14,21 @@ declare global {
|
|
|
9
14
|
keys?: Record<string | number, unknown>;
|
|
10
15
|
}
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* gets a named property of the specified game object or component
|
|
19
|
+
*
|
|
20
|
+
* @param url - url of the game object or component having the property
|
|
21
|
+
* @param property - id of the property to retrieve
|
|
22
|
+
* @param options - optional options table
|
|
23
|
+
* - index number index into array property (1 based)
|
|
24
|
+
* - key hash name of internal property
|
|
25
|
+
* - keys table array of internal component resources identified by key (e.g. a particle fx emitter, see examples below)
|
|
26
|
+
* @returns the value of the specified property
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const position = go.get("#sprite", "position");
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
12
32
|
function get<K extends keyof go.properties>(
|
|
13
33
|
url: string | Hash | Url,
|
|
14
34
|
property: K,
|
|
@@ -19,6 +39,21 @@ declare global {
|
|
|
19
39
|
property: string | Hash,
|
|
20
40
|
options?: GoPropertyOptions,
|
|
21
41
|
): number | boolean | Hash | Url | Vector3 | Vector4 | Quaternion | Opaque<"resource">;
|
|
42
|
+
/**
|
|
43
|
+
* sets a named property of the specified game object or component, or a material constant
|
|
44
|
+
*
|
|
45
|
+
* @param url - url of the game object or component having the property
|
|
46
|
+
* @param property - id of the property to set
|
|
47
|
+
* @param value - the value to set
|
|
48
|
+
* @param options - optional options table
|
|
49
|
+
* - index integer index into array property (1 based)
|
|
50
|
+
* - key hash name of internal property
|
|
51
|
+
* - keys table array of internal component resources identified by key (e.g. a particle fx emitter, see examples below)
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* go.set("#sprite", "tint", vmath.vector4(1, 0, 0, 1));
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
22
57
|
function set<K extends keyof go.properties>(
|
|
23
58
|
url: string | Hash | Url,
|
|
24
59
|
property: K,
|
|
@@ -31,5 +66,43 @@ declare global {
|
|
|
31
66
|
value: number | boolean | Hash | Url | Vector3 | Vector4 | Quaternion | Opaque<"resource">,
|
|
32
67
|
options?: GoPropertyOptions,
|
|
33
68
|
): void;
|
|
69
|
+
/**
|
|
70
|
+
* Registers a Defold editor script property (deprecated escape hatch).
|
|
71
|
+
*
|
|
72
|
+
* @deprecated Don't call `go.property` yourself. Declare the property in
|
|
73
|
+
* `defineScript({ properties })` — that is the only form that types it onto
|
|
74
|
+
* `self`; the transpiler emits the `go.property(...)` registration for you.
|
|
75
|
+
* A direct call still registers, but `self.<name>` stays untyped.
|
|
76
|
+
*
|
|
77
|
+
* @param name - editor property id to register.
|
|
78
|
+
* @param value - default value for the registered property.
|
|
79
|
+
* @returns a phantom descriptor used only for TypeScript self typing.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* // Declare it as a field — the key is the name, the value is the default:
|
|
83
|
+
* export default defineScript({
|
|
84
|
+
* properties: { speed: 450 },
|
|
85
|
+
* update(self) {
|
|
86
|
+
* self.speed; // number
|
|
87
|
+
* },
|
|
88
|
+
* });
|
|
89
|
+
* // The transpiler emits, at chunk scope: go.property("speed", 450)
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
function property(name: string, value: number): ScriptProperty<number>;
|
|
93
|
+
/** @deprecated Declare booleans via the `defineScript({ properties })` field. */
|
|
94
|
+
function property(name: string, value: boolean): ScriptProperty<boolean>;
|
|
95
|
+
/** @deprecated Declare hashes via the `defineScript({ properties })` field. */
|
|
96
|
+
function property(name: string, value: Hash): ScriptProperty<Hash>;
|
|
97
|
+
/** @deprecated Declare URLs via the `defineScript({ properties })` field. */
|
|
98
|
+
function property(name: string, value: Url): ScriptProperty<Url>;
|
|
99
|
+
/** @deprecated Declare vector3s via the `defineScript({ properties })` field. */
|
|
100
|
+
function property(name: string, value: Vector3): ScriptProperty<Vector3>;
|
|
101
|
+
/** @deprecated Declare vector4s via the `defineScript({ properties })` field. */
|
|
102
|
+
function property(name: string, value: Vector4): ScriptProperty<Vector4>;
|
|
103
|
+
/** @deprecated Declare quaternions via the `defineScript({ properties })` field. */
|
|
104
|
+
function property(name: string, value: Quaternion): ScriptProperty<Quaternion>;
|
|
105
|
+
/** @deprecated Declare resources via the `defineScript({ properties })` field. */
|
|
106
|
+
function property(name: string, value: Opaque<"resource">): ScriptProperty<Opaque<"resource">>;
|
|
34
107
|
}
|
|
35
108
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,11 +17,16 @@ export {
|
|
|
17
17
|
defineRenderScript,
|
|
18
18
|
defineScript,
|
|
19
19
|
type GuiScriptHooks,
|
|
20
|
+
type GuiScriptHooksWithProperties,
|
|
20
21
|
type InputAction,
|
|
21
22
|
type InputTouch,
|
|
22
23
|
type RenderScriptHooks,
|
|
24
|
+
type RenderScriptHooksWithProperties,
|
|
23
25
|
SCRIPT_HOOK_NAMES,
|
|
24
26
|
type ScriptHookName,
|
|
25
27
|
type ScriptHooks,
|
|
28
|
+
type ScriptHooksWithProperties,
|
|
29
|
+
type ScriptProperties,
|
|
30
|
+
type ScriptProperty,
|
|
26
31
|
} from "./lifecycle";
|
|
27
32
|
export { type WrapOptions, wrapAsAmbientGlobal } from "./publish-dts";
|