@defold-typescript/types 0.6.0 → 0.8.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/README.md +2 -2
- package/api-targets.json +2 -0
- package/generated/collectionproxy.d.ts +1 -1
- package/generated/go.d.ts +1 -1
- package/generated/gui.d.ts +1 -1
- package/generated/html5.d.ts +49 -0
- package/generated/http.d.ts +1 -1
- package/generated/kinds/gui-script.d.ts +4 -0
- package/generated/kinds/render-script.d.ts +4 -0
- package/generated/kinds/script.d.ts +4 -0
- package/generated/physics.d.ts +3 -3
- package/generated/profiler.d.ts +1 -1
- package/generated/render.d.ts +3 -3
- package/generated/resource.d.ts +3 -3
- package/generated/types.d.ts +59 -0
- package/index.d.ts +4 -0
- package/package.json +14 -1
- package/scripts/doc-source.ts +71 -5
- package/scripts/fidelity-audit.ts +48 -10
- package/scripts/fidelity-baseline.json +31 -15
- package/scripts/regen.ts +13 -1
- package/scripts/sync-api-docs.ts +6 -0
- package/src/emit-dts.ts +275 -5
- package/src/go-overloads.d.ts +17 -0
- package/src/lifecycle.ts +34 -9
- package/src/timers.d.ts +47 -0
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"collectionfactory": {
|
|
27
27
|
"droppedElements": 0,
|
|
28
28
|
"unknownTokens": [],
|
|
29
|
-
"recordTables":
|
|
29
|
+
"recordTables": 0,
|
|
30
30
|
"multiReturn": 0,
|
|
31
31
|
"droppedMembers": 0,
|
|
32
32
|
"optionalAsRequired": 0
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"collectionproxy": {
|
|
35
35
|
"droppedElements": 0,
|
|
36
36
|
"unknownTokens": [],
|
|
37
|
-
"recordTables":
|
|
37
|
+
"recordTables": 0,
|
|
38
38
|
"multiReturn": 0,
|
|
39
39
|
"droppedMembers": 0,
|
|
40
40
|
"optionalAsRequired": 0
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"crash": {
|
|
43
43
|
"droppedElements": 0,
|
|
44
44
|
"unknownTokens": [],
|
|
45
|
-
"recordTables":
|
|
45
|
+
"recordTables": 0,
|
|
46
46
|
"multiReturn": 0,
|
|
47
47
|
"droppedMembers": 0,
|
|
48
48
|
"optionalAsRequired": 0
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"factory": {
|
|
51
51
|
"droppedElements": 0,
|
|
52
52
|
"unknownTokens": [],
|
|
53
|
-
"recordTables":
|
|
53
|
+
"recordTables": 0,
|
|
54
54
|
"multiReturn": 0,
|
|
55
55
|
"droppedMembers": 0,
|
|
56
56
|
"optionalAsRequired": 0
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
"go": {
|
|
59
59
|
"droppedElements": 0,
|
|
60
60
|
"unknownTokens": [],
|
|
61
|
-
"recordTables":
|
|
61
|
+
"recordTables": 0,
|
|
62
62
|
"multiReturn": 0,
|
|
63
|
-
"droppedMembers":
|
|
63
|
+
"droppedMembers": 0,
|
|
64
64
|
"optionalAsRequired": 0
|
|
65
65
|
},
|
|
66
66
|
"graphics": {
|
|
@@ -74,7 +74,15 @@
|
|
|
74
74
|
"gui": {
|
|
75
75
|
"droppedElements": 0,
|
|
76
76
|
"unknownTokens": [],
|
|
77
|
-
"recordTables":
|
|
77
|
+
"recordTables": 0,
|
|
78
|
+
"multiReturn": 0,
|
|
79
|
+
"droppedMembers": 0,
|
|
80
|
+
"optionalAsRequired": 0
|
|
81
|
+
},
|
|
82
|
+
"html5": {
|
|
83
|
+
"droppedElements": 0,
|
|
84
|
+
"unknownTokens": [],
|
|
85
|
+
"recordTables": 0,
|
|
78
86
|
"multiReturn": 0,
|
|
79
87
|
"droppedMembers": 0,
|
|
80
88
|
"optionalAsRequired": 0
|
|
@@ -82,7 +90,7 @@
|
|
|
82
90
|
"http": {
|
|
83
91
|
"droppedElements": 0,
|
|
84
92
|
"unknownTokens": [],
|
|
85
|
-
"recordTables":
|
|
93
|
+
"recordTables": 0,
|
|
86
94
|
"multiReturn": 0,
|
|
87
95
|
"droppedMembers": 0,
|
|
88
96
|
"optionalAsRequired": 0
|
|
@@ -148,7 +156,7 @@
|
|
|
148
156
|
"unknownTokens": [],
|
|
149
157
|
"recordTables": 0,
|
|
150
158
|
"multiReturn": 0,
|
|
151
|
-
"droppedMembers":
|
|
159
|
+
"droppedMembers": 0,
|
|
152
160
|
"optionalAsRequired": 0
|
|
153
161
|
},
|
|
154
162
|
"particlefx": {
|
|
@@ -162,7 +170,7 @@
|
|
|
162
170
|
"physics": {
|
|
163
171
|
"droppedElements": 0,
|
|
164
172
|
"unknownTokens": [],
|
|
165
|
-
"recordTables":
|
|
173
|
+
"recordTables": 0,
|
|
166
174
|
"multiReturn": 0,
|
|
167
175
|
"droppedMembers": 0,
|
|
168
176
|
"optionalAsRequired": 0
|
|
@@ -170,7 +178,7 @@
|
|
|
170
178
|
"profiler": {
|
|
171
179
|
"droppedElements": 0,
|
|
172
180
|
"unknownTokens": [],
|
|
173
|
-
"recordTables":
|
|
181
|
+
"recordTables": 0,
|
|
174
182
|
"multiReturn": 0,
|
|
175
183
|
"droppedMembers": 0,
|
|
176
184
|
"optionalAsRequired": 0
|
|
@@ -186,7 +194,7 @@
|
|
|
186
194
|
"render": {
|
|
187
195
|
"droppedElements": 0,
|
|
188
196
|
"unknownTokens": [],
|
|
189
|
-
"recordTables":
|
|
197
|
+
"recordTables": 0,
|
|
190
198
|
"multiReturn": 0,
|
|
191
199
|
"droppedMembers": 0,
|
|
192
200
|
"optionalAsRequired": 0
|
|
@@ -194,7 +202,7 @@
|
|
|
194
202
|
"resource": {
|
|
195
203
|
"droppedElements": 0,
|
|
196
204
|
"unknownTokens": [],
|
|
197
|
-
"recordTables":
|
|
205
|
+
"recordTables": 0,
|
|
198
206
|
"multiReturn": 0,
|
|
199
207
|
"droppedMembers": 0,
|
|
200
208
|
"optionalAsRequired": 0
|
|
@@ -202,7 +210,7 @@
|
|
|
202
210
|
"socket": {
|
|
203
211
|
"droppedElements": 0,
|
|
204
212
|
"unknownTokens": [],
|
|
205
|
-
"recordTables":
|
|
213
|
+
"recordTables": 0,
|
|
206
214
|
"multiReturn": 0,
|
|
207
215
|
"droppedMembers": 0,
|
|
208
216
|
"optionalAsRequired": 0
|
|
@@ -247,6 +255,14 @@
|
|
|
247
255
|
"droppedMembers": 0,
|
|
248
256
|
"optionalAsRequired": 0
|
|
249
257
|
},
|
|
258
|
+
"types": {
|
|
259
|
+
"droppedElements": 0,
|
|
260
|
+
"unknownTokens": [],
|
|
261
|
+
"recordTables": 0,
|
|
262
|
+
"multiReturn": 0,
|
|
263
|
+
"droppedMembers": 0,
|
|
264
|
+
"optionalAsRequired": 0
|
|
265
|
+
},
|
|
250
266
|
"vmath": {
|
|
251
267
|
"droppedElements": 0,
|
|
252
268
|
"unknownTokens": [],
|
|
@@ -258,7 +274,7 @@
|
|
|
258
274
|
"webview": {
|
|
259
275
|
"droppedElements": 0,
|
|
260
276
|
"unknownTokens": [],
|
|
261
|
-
"recordTables":
|
|
277
|
+
"recordTables": 0,
|
|
262
278
|
"multiReturn": 0,
|
|
263
279
|
"droppedMembers": 0,
|
|
264
280
|
"optionalAsRequired": 0
|
package/scripts/regen.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { wrapAsAmbientGlobal } from "../src/publish-dts";
|
|
|
9
9
|
import {
|
|
10
10
|
type DocSourceProvenance,
|
|
11
11
|
type DownloadRefDoc,
|
|
12
|
+
type FetchChannelInfo,
|
|
13
|
+
type RefDocChannel,
|
|
12
14
|
refDocCacheDir,
|
|
13
15
|
resolveRefDoc,
|
|
14
16
|
} from "./doc-source";
|
|
@@ -89,6 +91,8 @@ export interface ResolveTargetOptions {
|
|
|
89
91
|
readonly readZip?: typeof readZip;
|
|
90
92
|
readonly syncManifest?: readonly SyncManifestEntry[];
|
|
91
93
|
readonly packageRoot?: string;
|
|
94
|
+
readonly channel?: RefDocChannel;
|
|
95
|
+
readonly fetchChannelInfo?: FetchChannelInfo;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
// Source-aware module resolution: a `null`-source target reads committed
|
|
@@ -106,8 +110,10 @@ export async function resolveTargetModules(
|
|
|
106
110
|
const { zip, provenance } = await resolveRefDoc({
|
|
107
111
|
version: source.version,
|
|
108
112
|
cacheDir: opts.cacheDir ?? refDocCacheDir(),
|
|
113
|
+
...(opts.channel ? { channel: opts.channel } : {}),
|
|
109
114
|
...(opts.download ? { download: opts.download } : {}),
|
|
110
115
|
...(opts.readZip ? { readZip: opts.readZip } : {}),
|
|
116
|
+
...(opts.fetchChannelInfo ? { fetchChannelInfo: opts.fetchChannelInfo } : {}),
|
|
111
117
|
});
|
|
112
118
|
const syncManifest = opts.syncManifest ?? SYNC_MANIFEST;
|
|
113
119
|
return target.modules.map((module) => {
|
|
@@ -217,6 +223,12 @@ export const RESTRICTED_NAMESPACES: Readonly<Record<string, string>> = {
|
|
|
217
223
|
render: "render_script",
|
|
218
224
|
};
|
|
219
225
|
|
|
226
|
+
// The Lua standard library rides every per-kind subpath the same as the full
|
|
227
|
+
// entrypoint. Triple-slash directives must precede the first statement, so they
|
|
228
|
+
// lead the generated kind index.
|
|
229
|
+
const LUA_STDLIB_REFERENCES =
|
|
230
|
+
'/// <reference types="lua-types/5.1" />\n/// <reference types="lua-types/special/jit-only" />\n';
|
|
231
|
+
|
|
220
232
|
const UNIVERSAL_EXTRA_IMPORTS: readonly string[] = [
|
|
221
233
|
"../builtin-messages",
|
|
222
234
|
"../../src/engine-globals",
|
|
@@ -247,7 +259,7 @@ export function generateKindIndex(kind: string): string {
|
|
|
247
259
|
...new Set([...universalNamespaces.sort(), ...[...UNIVERSAL_EXTRA_IMPORTS].sort()]),
|
|
248
260
|
].map((path) => `import "${path}";`);
|
|
249
261
|
if (entry.restricted) lines.push(`import "../${entry.restricted}";`);
|
|
250
|
-
return `${lines.join("\n")}\n\nexport { ${entry.factory} } from "../../src/lifecycle";\nexport type { ScriptProperties, ScriptProperty } from "../../src/lifecycle";\n`;
|
|
262
|
+
return `${LUA_STDLIB_REFERENCES}${lines.join("\n")}\n\nexport { ${entry.factory} } from "../../src/lifecycle";\nexport type { ScriptProperties, ScriptProperty } from "../../src/lifecycle";\n`;
|
|
251
263
|
}
|
|
252
264
|
|
|
253
265
|
export function generateVersionIndex(
|
package/scripts/sync-api-docs.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const SYNC_MANIFEST: readonly SyncManifestEntry[] = [
|
|
|
30
30
|
entry("go", "doc/gameobject_script.cpp_doc.json"),
|
|
31
31
|
entry("graphics", "doc/src-script_graphics.cpp_doc.json"),
|
|
32
32
|
entry("gui", "doc/gui_script.cpp_doc.json"),
|
|
33
|
+
entry("html5", "doc/src-script_html5_js.cpp_doc.json"),
|
|
33
34
|
entry("http", "doc/scripts-script_http.cpp_doc.json"),
|
|
34
35
|
entry("image", "doc/scripts-script_image.cpp_doc.json"),
|
|
35
36
|
entry("json", "doc/src-script_json.cpp_doc.json"),
|
|
@@ -50,6 +51,7 @@ export const SYNC_MANIFEST: readonly SyncManifestEntry[] = [
|
|
|
50
51
|
entry("sys", "doc/src-script_sys.cpp_doc.json"),
|
|
51
52
|
entry("tilemap", "doc/scripts-script_tilemap.cpp_doc.json"),
|
|
52
53
|
entry("timer", "doc/src-script_timer.cpp_doc.json"),
|
|
54
|
+
entry("types", "doc/src-script_types.cpp_doc.json"),
|
|
53
55
|
entry("vmath", "doc/src-script_vmath.cpp_doc.json"),
|
|
54
56
|
entry("window", "doc/scripts-script_window.cpp_doc.json"),
|
|
55
57
|
entry("zlib", "doc/src-script_zlib.cpp_doc.json"),
|
|
@@ -123,6 +125,9 @@ export function parseChecklistNamespaces(visionMarkdown: string): string[] {
|
|
|
123
125
|
export interface ZipAccessor {
|
|
124
126
|
has(entry: string): boolean;
|
|
125
127
|
read(entry: string): string;
|
|
128
|
+
// Surfaces the same entry set `has` is built from, so the extension-archive
|
|
129
|
+
// resolver can hand the list to the pure `locateScriptApis` / `classifyExtension`.
|
|
130
|
+
entries(): string[];
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
export function readZip(path: string): ZipAccessor {
|
|
@@ -133,6 +138,7 @@ export function readZip(path: string): ZipAccessor {
|
|
|
133
138
|
const entries = new Set(list.stdout.toString().split("\n").filter(Boolean));
|
|
134
139
|
return {
|
|
135
140
|
has: (name) => entries.has(name),
|
|
141
|
+
entries: () => [...entries],
|
|
136
142
|
read: (name) => {
|
|
137
143
|
const out = Bun.spawnSync(["unzip", "-p", path, name]);
|
|
138
144
|
if (out.exitCode !== 0) {
|
package/src/emit-dts.ts
CHANGED
|
@@ -82,6 +82,20 @@ export const TS_RESERVED_NAMES = new Set([
|
|
|
82
82
|
"extends",
|
|
83
83
|
]);
|
|
84
84
|
|
|
85
|
+
// A parameter name that clears `TS_IDENTIFIER` but is a TS reserved word (e.g.
|
|
86
|
+
// `var` on the `types.is_*` guards) is illegal in parameter position (TS1390),
|
|
87
|
+
// unlike a member name which `TS_RESERVED_NAMES` recovers via an export alias.
|
|
88
|
+
// Parameter names in a `.d.ts` are non-binding, so a reserved word takes the
|
|
89
|
+
// idiomatic trailing-underscore escape (`var` -> `var_`, matching
|
|
90
|
+
// ts-defold-types so the parity audit stays a superset); a genuinely
|
|
91
|
+
// non-identifier token still falls back to the positional `arg<index>` form.
|
|
92
|
+
// The emitted signature and its `@param` tag share this name so the tag stays
|
|
93
|
+
// resolvable on hover.
|
|
94
|
+
function safeParamName(name: string, index: number): string {
|
|
95
|
+
if (!TS_IDENTIFIER.test(name)) return `arg${index}`;
|
|
96
|
+
return TS_RESERVED_NAMES.has(name) ? `${name}_` : name;
|
|
97
|
+
}
|
|
98
|
+
|
|
85
99
|
// Element names whose `table` slot is a genuinely-arbitrary lua table by design.
|
|
86
100
|
// Two kinds: the serialization/JSON passthrough functions (Defold-internal — the
|
|
87
101
|
// engine round-trips an arbitrary lua value), and the platform/OS-sourced opaque
|
|
@@ -101,6 +115,55 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
101
115
|
"iac.set_listener",
|
|
102
116
|
"push.get_scheduled",
|
|
103
117
|
"push.get_all_scheduled",
|
|
118
|
+
// runtime-owned passthrough: the table is filled by user code at runtime, never
|
|
119
|
+
// by Defold's API contract. factory.create/collectionfactory.create `properties`
|
|
120
|
+
// are spawn-time overrides keyed by the spawned object's own script-property
|
|
121
|
+
// names; bare `on_message`'s receive `message` is the pre-hashed, user-routed
|
|
122
|
+
// payload (mitigated for authored code by isMessage/onMessage). No static shape
|
|
123
|
+
// exists, so `Record<string | number, unknown>` is faithful, not a loss.
|
|
124
|
+
"factory.create",
|
|
125
|
+
"collectionfactory.create",
|
|
126
|
+
"on_message",
|
|
127
|
+
// engine-formatted blob: the engine produces the value at runtime and no
|
|
128
|
+
// field list is documented, so the whole-slot `Record` is faithful, not a
|
|
129
|
+
// loss. crash.get_backtrace is "table containing the backtrace",
|
|
130
|
+
// crash.get_modules is "module table".
|
|
131
|
+
"crash.get_backtrace",
|
|
132
|
+
"crash.get_modules",
|
|
133
|
+
// platform-specific options: the doc is a dead backtick-prose cross-ref to a
|
|
134
|
+
// sibling whose function is absent from the fixture (webview.open is not
|
|
135
|
+
// documented), so no machine-readable shape exists.
|
|
136
|
+
"webview.open_raw",
|
|
137
|
+
// OS-resolver returns: the shape is set by the host resolver, not Defold's
|
|
138
|
+
// API contract; each fixture doc reads "a table with all information
|
|
139
|
+
// returned by the resolver". Mirrors the iac.set_listener / push.* platform-
|
|
140
|
+
// opaque precedent.
|
|
141
|
+
"socket.dns.tohostname",
|
|
142
|
+
"socket.dns.toip",
|
|
143
|
+
"socket.dns.getaddrinfo",
|
|
144
|
+
"socket.dns.getnameinfo",
|
|
145
|
+
// engine-built render-target tables: the "see the description" cross-refs
|
|
146
|
+
// are dead (the function's own description is empty) or carry a typed
|
|
147
|
+
// `transient: table` field whose sub-doc is prose-only, so the nested table
|
|
148
|
+
// is opaque to the field-list parser and the whole-slot `Record` is faithful.
|
|
149
|
+
"render.render_target",
|
|
150
|
+
"render.set_render_target",
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
// skipFunction FQNs (see `api-targets.json`'s `skipFunctions`) whose absence
|
|
154
|
+
// from the generated surface is *not* a fidelity loss — each is replaced by a
|
|
155
|
+
// hand-written, better-typed overload in the cited `*-overloads.d.ts`, so the
|
|
156
|
+
// final surface still ships the symbol, fully typed. The audit consults this
|
|
157
|
+
// set to keep the `droppedMembers` count honest; an uncovered skip (e.g. a new
|
|
158
|
+
// `skipFunctions` entry with no matching overload) still counts, so the gate
|
|
159
|
+
// stays a real signal. Mirrors the ARBITRARY_TABLE_SLOTS audit-honesty pattern.
|
|
160
|
+
export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
161
|
+
// go-overloads.d.ts supplies the typed go.get / go.set / go.property shapes.
|
|
162
|
+
"go.get",
|
|
163
|
+
"go.set",
|
|
164
|
+
"go.property",
|
|
165
|
+
// msg-overloads.d.ts supplies the typed msg.post shape.
|
|
166
|
+
"msg.post",
|
|
104
167
|
]);
|
|
105
168
|
|
|
106
169
|
// Element names whose `table` slot is a prose-only `a table mapping X to Y`
|
|
@@ -288,6 +351,175 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
288
351
|
"tilemap.get_tiles:return:tiles",
|
|
289
352
|
{ kind: "mapping", key: "number", value: { key: "number", value: "number" } },
|
|
290
353
|
],
|
|
354
|
+
// resource.get_text_metrics's `metrics` return is an untyped name-only <ul>
|
|
355
|
+
// (width/height/max_ascent/max_descent, no <span class="type"> and no See
|
|
356
|
+
// cross-reference), so parseTableFields recovers nothing and the whole slot
|
|
357
|
+
// collapses to Record. The four fields are font metrics in pixels, i.e.
|
|
358
|
+
// `number`; as a return-table object curation they are required.
|
|
359
|
+
[
|
|
360
|
+
"resource.get_text_metrics:return:metrics",
|
|
361
|
+
{
|
|
362
|
+
kind: "object",
|
|
363
|
+
fields: [
|
|
364
|
+
{ name: "width", types: ["number"] },
|
|
365
|
+
{ name: "height", types: ["number"] },
|
|
366
|
+
{ name: "max_ascent", types: ["number"] },
|
|
367
|
+
{ name: "max_descent", types: ["number"] },
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
// profiler.view_recorded_frame's `frame_index` is a typed `<ul>` "specify one
|
|
372
|
+
// of the following": `distance` and `frame`, both numeric frame offsets. The
|
|
373
|
+
// <li> items carry no `<span class="type">`, so parseTableFields recovers
|
|
374
|
+
// nothing and the slot collapses to Record. As a param option bag both fields
|
|
375
|
+
// are optional (the caller supplies one), which the param-side `?` flag emits.
|
|
376
|
+
[
|
|
377
|
+
"profiler.view_recorded_frame:param:frame_index",
|
|
378
|
+
{
|
|
379
|
+
kind: "object",
|
|
380
|
+
fields: [
|
|
381
|
+
{ name: "distance", types: ["number"] },
|
|
382
|
+
{ name: "frame", types: ["number"] },
|
|
383
|
+
],
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
// http.request's `headers` is "optional table with custom headers", a
|
|
387
|
+
// string-keyed string map (no field list). Slot-path keying targets only
|
|
388
|
+
// `headers`; the sibling `options` table stays parser-recovered.
|
|
389
|
+
["http.request:param:headers", { kind: "mapping", key: "string", value: "string" }],
|
|
390
|
+
// collectionproxy.get_resources returns "the resources, or an empty list", a
|
|
391
|
+
// homogeneous list of resource-path hashes (prose only, no field list).
|
|
392
|
+
["collectionproxy.get_resources:return:resources", { kind: "array", element: "hash" }],
|
|
393
|
+
// render.predicate's `tags` is "table of tags ... can be of either hash or
|
|
394
|
+
// string type", a homogeneous array whose element is the string|hash union.
|
|
395
|
+
["render.predicate:param:tags", { kind: "array", element: ["string", "hash"] }],
|
|
396
|
+
// render.clear's `buffers` maps the numeric graphics.BUFFER_* constants to
|
|
397
|
+
// their clear value: vector4 for the color buffer, number for depth/stencil.
|
|
398
|
+
// The mapping value is the `number | vector4` union; the mapping string branch
|
|
399
|
+
// splits the union token, so the value emits `number | Vector4`.
|
|
400
|
+
["render.clear:param:buffers", { kind: "mapping", key: "number", value: "number | vector4" }],
|
|
401
|
+
// go.on_input and gui.on_input share the same well-known InputAction shape
|
|
402
|
+
// (the on_input function description lists the well-known fields: value,
|
|
403
|
+
// pressed, released, repeated, x/y/screen_x/screen_y/dx/dy/screen_dx/screen_dy,
|
|
404
|
+
// plus gamepad/touch/text multi-touch fields). The doc markup carries no
|
|
405
|
+
// field list of its own, so the shape is curated verbatim from the prose.
|
|
406
|
+
// Param-side optional fields: each input kind (mouse/key/text/gamepad) sets a
|
|
407
|
+
// different subset, so the call site always sees an `?` on every field.
|
|
408
|
+
// `touch` stays `table` (the nested multi-touch table has no machine-readable
|
|
409
|
+
// field list; mirrors the render.set_render_target.transient recursive case).
|
|
410
|
+
// The element is the bare `on_input` script hook (no namespace prefix), the
|
|
411
|
+
// same form the `on_message` arbitrary-table entry uses. The doc carries a
|
|
412
|
+
// well-formed touch input table, so `touch` is curated as a nested
|
|
413
|
+
// array-of-touch-records (the field has its own `<table>` listing id,
|
|
414
|
+
// pressed, released, tap_count, x, y, dx, dy, acc_x/y/z).
|
|
415
|
+
[
|
|
416
|
+
"on_input:param:action",
|
|
417
|
+
{
|
|
418
|
+
kind: "object",
|
|
419
|
+
fields: [
|
|
420
|
+
{ name: "value", types: ["number"], optional: true },
|
|
421
|
+
{ name: "pressed", types: ["boolean"], optional: true },
|
|
422
|
+
{ name: "released", types: ["boolean"], optional: true },
|
|
423
|
+
{ name: "repeated", types: ["boolean"], optional: true },
|
|
424
|
+
{ name: "x", types: ["number"], optional: true },
|
|
425
|
+
{ name: "y", types: ["number"], optional: true },
|
|
426
|
+
{ name: "screen_x", types: ["number"], optional: true },
|
|
427
|
+
{ name: "screen_y", types: ["number"], optional: true },
|
|
428
|
+
{ name: "dx", types: ["number"], optional: true },
|
|
429
|
+
{ name: "dy", types: ["number"], optional: true },
|
|
430
|
+
{ name: "screen_dx", types: ["number"], optional: true },
|
|
431
|
+
{ name: "screen_dy", types: ["number"], optional: true },
|
|
432
|
+
{ name: "gamepad", types: ["number"], optional: true },
|
|
433
|
+
{ name: "gamepad_axis", types: ["vector3"], optional: true },
|
|
434
|
+
{
|
|
435
|
+
name: "touch",
|
|
436
|
+
types: ["table"],
|
|
437
|
+
optional: true,
|
|
438
|
+
isList: true,
|
|
439
|
+
fields: [
|
|
440
|
+
{ name: "id", types: ["number"] },
|
|
441
|
+
{ name: "pressed", types: ["boolean"] },
|
|
442
|
+
{ name: "released", types: ["boolean"] },
|
|
443
|
+
{ name: "tap_count", types: ["number"] },
|
|
444
|
+
{ name: "x", types: ["number"] },
|
|
445
|
+
{ name: "y", types: ["number"] },
|
|
446
|
+
{ name: "dx", types: ["number"] },
|
|
447
|
+
{ name: "dy", types: ["number"] },
|
|
448
|
+
{ name: "acc_x", types: ["number"] },
|
|
449
|
+
{ name: "acc_y", types: ["number"] },
|
|
450
|
+
{ name: "acc_z", types: ["number"] },
|
|
451
|
+
],
|
|
452
|
+
},
|
|
453
|
+
{ name: "text", types: ["string"], optional: true },
|
|
454
|
+
],
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
// physics.create_joint and physics.set_joint_properties both declare
|
|
458
|
+
// `properties` as a joint-type-specific table; the only universal field is
|
|
459
|
+
// `collide_connected` (parseTableFields already recovers it from the
|
|
460
|
+
// <span class="type">+<code> shape in the create_joint doc and the bare
|
|
461
|
+
// <code> shape in the set_joint_properties doc). Joint-type-specific fields
|
|
462
|
+
// stay deferred — they live in joint-type-specific docs the parser cannot
|
|
463
|
+
// see from a top-level param doc.
|
|
464
|
+
[
|
|
465
|
+
"physics.create_joint:param:properties",
|
|
466
|
+
{
|
|
467
|
+
kind: "object",
|
|
468
|
+
fields: [{ name: "collide_connected", types: ["boolean"], optional: true }],
|
|
469
|
+
},
|
|
470
|
+
],
|
|
471
|
+
[
|
|
472
|
+
"physics.set_joint_properties:param:properties",
|
|
473
|
+
{
|
|
474
|
+
kind: "object",
|
|
475
|
+
fields: [{ name: "collide_connected", types: ["boolean"], optional: true }],
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
// physics.raycast's `result` is "a list … see ray_cast_response for details";
|
|
479
|
+
// buildTableDocResolver resolves only FUNCTION elements so the cross-ref
|
|
480
|
+
// cannot fire (the message payload lives in messages_doc.json, not the
|
|
481
|
+
// FUNCTION slot pool). The shape is curated directly from the message
|
|
482
|
+
// catalog (the six ray_cast_response payload fields). The doc declares a
|
|
483
|
+
// `nil` branch ("If missed it returns <code>nil</code>"), so the existing
|
|
484
|
+
// nil-aware emit already projects the curated array onto the
|
|
485
|
+
// `array-object | nil` return type — no curation change needed there.
|
|
486
|
+
[
|
|
487
|
+
"physics.raycast:return:result",
|
|
488
|
+
{
|
|
489
|
+
kind: "array-object",
|
|
490
|
+
fields: [
|
|
491
|
+
{ name: "fraction", types: ["number"] },
|
|
492
|
+
{ name: "position", types: ["vector3"] },
|
|
493
|
+
{ name: "normal", types: ["vector3"] },
|
|
494
|
+
{ name: "id", types: ["hash"] },
|
|
495
|
+
{ name: "group", types: ["hash"] },
|
|
496
|
+
{ name: "request_id", types: ["number"] },
|
|
497
|
+
],
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
]);
|
|
501
|
+
|
|
502
|
+
// resource.set_atlas's `table` param and resource.get_atlas's `data` return are
|
|
503
|
+
// flattened `<li><dl>` field lists whose `geometries` header is followed by the
|
|
504
|
+
// `table`-typed siblings `vertices`/`uvs`/`indices` — the grouping heuristic
|
|
505
|
+
// stops at the next `table` header, so `geometries` is left memberless and
|
|
506
|
+
// collapses to `Record`. The doc markup carries no signal that those number-list
|
|
507
|
+
// fields nest *under* `geometries` rather than beside it, so the nesting is
|
|
508
|
+
// hand-curated. Each geometry is the triangle data (the create-input form
|
|
509
|
+
// get_atlas mirrors via its cross-ref), and `vertices`/`uvs`/`indices` are
|
|
510
|
+
// brace-form number-lists, hence `numberList: true` (emitted `number[]`).
|
|
511
|
+
// Injected onto the already-parsed field list (every sibling stays
|
|
512
|
+
// parser-authoritative), so this is a nested-field curation, not a whole-slot
|
|
513
|
+
// object curation.
|
|
514
|
+
const ATLAS_GEOMETRY_MEMBERS: readonly TableField[] = [
|
|
515
|
+
{ name: "vertices", types: ["table"], numberList: true },
|
|
516
|
+
{ name: "uvs", types: ["table"], numberList: true },
|
|
517
|
+
{ name: "indices", types: ["table"], numberList: true },
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
export const NESTED_FIELD_CURATIONS: ReadonlyMap<string, readonly TableField[]> = new Map([
|
|
521
|
+
["resource.set_atlas:param:table:geometries", ATLAS_GEOMETRY_MEMBERS],
|
|
522
|
+
["resource.get_atlas:return:data:geometries", ATLAS_GEOMETRY_MEMBERS],
|
|
291
523
|
]);
|
|
292
524
|
|
|
293
525
|
/**
|
|
@@ -415,6 +647,7 @@ export interface TableField {
|
|
|
415
647
|
fields?: TableField[];
|
|
416
648
|
isList?: boolean;
|
|
417
649
|
numberList?: boolean;
|
|
650
|
+
optional?: boolean;
|
|
418
651
|
}
|
|
419
652
|
|
|
420
653
|
function parseUlFields(doc: string): TableField[] {
|
|
@@ -783,7 +1016,7 @@ function emitFunction(
|
|
|
783
1016
|
// for a fully-undocumented function, leaving its emission byte-identical.
|
|
784
1017
|
function functionDocLines(fn: ApiFunction, translations: TranslationStore): string[] {
|
|
785
1018
|
const params = fn.parameters.map((p, index) => ({
|
|
786
|
-
name:
|
|
1019
|
+
name: safeParamName(p.name, index),
|
|
787
1020
|
doc: htmlToDocText(p.doc),
|
|
788
1021
|
}));
|
|
789
1022
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
@@ -853,7 +1086,7 @@ function emitParameter(
|
|
|
853
1086
|
resolver: TableDocResolver,
|
|
854
1087
|
elementName: string,
|
|
855
1088
|
): string {
|
|
856
|
-
const name =
|
|
1089
|
+
const name = safeParamName(p.name, index);
|
|
857
1090
|
const concrete = p.types.filter((t) => t !== "nil");
|
|
858
1091
|
const ts =
|
|
859
1092
|
concrete.length > 0
|
|
@@ -937,7 +1170,13 @@ function mapSlotUnion(
|
|
|
937
1170
|
if (mapping !== undefined) {
|
|
938
1171
|
let value: string;
|
|
939
1172
|
if (typeof mapping.value === "string") {
|
|
940
|
-
value
|
|
1173
|
+
// A mapping value may be a `T | U` union token (render.clear's
|
|
1174
|
+
// `number | vector4` clear value); split on `|` so each token maps
|
|
1175
|
+
// individually. A single-token value (no `|`) is unaffected.
|
|
1176
|
+
value = unionFromTokens(
|
|
1177
|
+
mapping.value.split("|").map((token) => token.trim()),
|
|
1178
|
+
mapType,
|
|
1179
|
+
);
|
|
941
1180
|
} else if (Array.isArray(mapping.value)) {
|
|
942
1181
|
value = inlineTableType(mapping.value, mapType, optionalFields);
|
|
943
1182
|
} else {
|
|
@@ -951,8 +1190,9 @@ function mapSlotUnion(
|
|
|
951
1190
|
const object = inlineTableType(curation.fields, mapType, optionalFields);
|
|
952
1191
|
ts = curation.kind === "array-object" ? `${object}[]` : object;
|
|
953
1192
|
} else {
|
|
954
|
-
const
|
|
955
|
-
if (
|
|
1193
|
+
const parsed = parseTableFields(doc, resolver);
|
|
1194
|
+
if (parsed !== null) {
|
|
1195
|
+
const fields = applyNestedFieldCurations(elementName, slotKind, slotName, parsed);
|
|
956
1196
|
const object = inlineTableType(fields, mapType, optionalFields);
|
|
957
1197
|
ts = isSlotLevelList(doc) ? `${object}[]` : object;
|
|
958
1198
|
} else {
|
|
@@ -973,6 +1213,36 @@ function tableSlotKey(elementName: string, slotKind: "param" | "return", slotNam
|
|
|
973
1213
|
return `${elementName}:${slotKind}:${slotName}`;
|
|
974
1214
|
}
|
|
975
1215
|
|
|
1216
|
+
function nestedFieldKey(
|
|
1217
|
+
elementName: string,
|
|
1218
|
+
slotKind: "param" | "return",
|
|
1219
|
+
slotName: string,
|
|
1220
|
+
fieldName: string,
|
|
1221
|
+
): string {
|
|
1222
|
+
return `${tableSlotKey(elementName, slotKind, slotName)}:${fieldName}`;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// Inject the curated nested members of NESTED_FIELD_CURATIONS onto the matching
|
|
1226
|
+
// top-level field of an otherwise parser-recovered slot, leaving every sibling
|
|
1227
|
+
// parser-authoritative. A field the parser already gave nested fields is left
|
|
1228
|
+
// alone (parser won). Returns a new array; never mutates the parser's result.
|
|
1229
|
+
export function applyNestedFieldCurations(
|
|
1230
|
+
elementName: string,
|
|
1231
|
+
slotKind: "param" | "return" | undefined,
|
|
1232
|
+
slotName: string | undefined,
|
|
1233
|
+
fields: readonly TableField[],
|
|
1234
|
+
): TableField[] {
|
|
1235
|
+
if (slotKind === undefined || slotName === undefined) return [...fields];
|
|
1236
|
+
return fields.map((field) => {
|
|
1237
|
+
if (field.fields !== undefined) return field;
|
|
1238
|
+
const curated = NESTED_FIELD_CURATIONS.get(
|
|
1239
|
+
nestedFieldKey(elementName, slotKind, slotName, field.name),
|
|
1240
|
+
);
|
|
1241
|
+
if (curated === undefined) return field;
|
|
1242
|
+
return { ...field, fields: [...curated], isList: true };
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
|
|
976
1246
|
function arrayTypeFromTokens(
|
|
977
1247
|
element: string | readonly string[],
|
|
978
1248
|
mapType: (t: string) => string,
|
package/src/go-overloads.d.ts
CHANGED
|
@@ -27,8 +27,16 @@ declare global {
|
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
29
|
* const position = go.get("#sprite", "position");
|
|
30
|
+
* // Name the target component to read its catalogued property type. The
|
|
31
|
+
* // empty call applies the type argument, then the inner call infers the key:
|
|
32
|
+
* const animation = go.get<sprite.properties>()("#sprite", "animation"); // Hash
|
|
30
33
|
* ```
|
|
31
34
|
*/
|
|
35
|
+
function get<P>(): <K extends keyof P>(
|
|
36
|
+
url: string | Hash | Url,
|
|
37
|
+
property: K,
|
|
38
|
+
options?: GoPropertyOptions,
|
|
39
|
+
) => P[K];
|
|
32
40
|
function get<K extends keyof go.properties>(
|
|
33
41
|
url: string | Hash | Url,
|
|
34
42
|
property: K,
|
|
@@ -52,8 +60,17 @@ declare global {
|
|
|
52
60
|
* @example
|
|
53
61
|
* ```ts
|
|
54
62
|
* go.set("#sprite", "tint", vmath.vector4(1, 0, 0, 1));
|
|
63
|
+
* // Name the target component to gate the value to its property type. The
|
|
64
|
+
* // empty call applies the type argument, then the inner call infers the key:
|
|
65
|
+
* go.set<sprite.properties>()("#sprite", "playback_rate", 2);
|
|
55
66
|
* ```
|
|
56
67
|
*/
|
|
68
|
+
function set<P>(): <K extends keyof P>(
|
|
69
|
+
url: string | Hash | Url,
|
|
70
|
+
property: K,
|
|
71
|
+
value: P[K],
|
|
72
|
+
options?: GoPropertyOptions,
|
|
73
|
+
) => void;
|
|
57
74
|
function set<K extends keyof go.properties>(
|
|
58
75
|
url: string | Hash | Url,
|
|
59
76
|
property: K,
|
package/src/lifecycle.ts
CHANGED
|
@@ -120,27 +120,52 @@ export type RenderScriptHooks<TSelf, TInitState = TSelf> = Omit<
|
|
|
120
120
|
// callbacks see (`NoInfer`-wrapped inside the hook set), and `TInitState` what
|
|
121
121
|
// `init` returns. `ScriptHooks` itself stays callback-only so the
|
|
122
122
|
// `SCRIPT_HOOK_NAMES` drift pin remains valid.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
//
|
|
124
|
+
// `init` is overridden to receive `self: NoInfer<TProps>` — Defold applies the
|
|
125
|
+
// declared property values to `self` before `init` runs, so init-time setup can
|
|
126
|
+
// read them. `self` is *only* the property channel (`TProps`), not the merged
|
|
127
|
+
// `TSelf`: the return is still the sole `TInitState` inference site, and
|
|
128
|
+
// `NoInfer<TProps>` keeps `self` from competing with `properties` as a second
|
|
129
|
+
// `TProps` inference site (the non-circularity the no-`self` `init` originally
|
|
130
|
+
// bought).
|
|
131
|
+
export type ScriptHooksWithProperties<TProps, TSelf, TInitState> = Omit<
|
|
132
|
+
ScriptHooks<TSelf, TInitState>,
|
|
133
|
+
"init"
|
|
126
134
|
> & {
|
|
135
|
+
init?(self: NoInfer<TProps>): TInitState;
|
|
127
136
|
properties?: TProps;
|
|
128
137
|
};
|
|
129
138
|
|
|
130
|
-
export type GuiScriptHooksWithProperties<TProps, TSelf, TInitState> =
|
|
131
|
-
TSelf,
|
|
132
|
-
|
|
139
|
+
export type GuiScriptHooksWithProperties<TProps, TSelf, TInitState> = Omit<
|
|
140
|
+
GuiScriptHooks<TSelf, TInitState>,
|
|
141
|
+
"init"
|
|
133
142
|
> & {
|
|
143
|
+
init?(self: NoInfer<TProps>): TInitState;
|
|
134
144
|
properties?: TProps;
|
|
135
145
|
};
|
|
136
146
|
|
|
137
|
-
export type RenderScriptHooksWithProperties<TProps, TSelf, TInitState> =
|
|
138
|
-
TSelf,
|
|
139
|
-
|
|
147
|
+
export type RenderScriptHooksWithProperties<TProps, TSelf, TInitState> = Omit<
|
|
148
|
+
RenderScriptHooks<TSelf, TInitState>,
|
|
149
|
+
"init"
|
|
140
150
|
> & {
|
|
151
|
+
init?(self: NoInfer<TProps>): TInitState;
|
|
141
152
|
properties?: TProps;
|
|
142
153
|
};
|
|
143
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Extract a script module's declared property channel (`TProps`) as a nameable
|
|
157
|
+
* type. A script declares its editor properties with the value-keyed
|
|
158
|
+
* `properties` field of `defineScript`; another module reads that shape with
|
|
159
|
+
* `ScriptPropertiesOf<typeof script>` and names it as the `P` generic of
|
|
160
|
+
* `go.get`/`go.set` to read or tune those properties cross-script by URL (e.g.
|
|
161
|
+
* `go.get<ScriptPropertiesOf<typeof enemy>>()("/enemy#controller", "speed")`).
|
|
162
|
+
*
|
|
163
|
+
* It keeps one source of truth: the extracted shape is the same `TProps` the
|
|
164
|
+
* owning script's `self` exposes, so there is no second hand-maintained
|
|
165
|
+
* interface to drift.
|
|
166
|
+
*/
|
|
167
|
+
export type ScriptPropertiesOf<T extends { properties?: object }> = NonNullable<T["properties"]>;
|
|
168
|
+
|
|
144
169
|
/**
|
|
145
170
|
* Type a `.script` component's hook table. At runtime this is an identity
|
|
146
171
|
* function — it returns `hooks` unchanged; its only job is typing. It infers
|