@defold-typescript/library-types 0.22.0 → 0.24.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-doc/boom.json +3870 -0
- package/api-doc/{bridge.bridge.json → bridge.json} +754 -844
- package/api-doc/bzAnim.json +338 -0
- package/api-doc/decore.json +23 -23
- package/api-doc/defcon.json +150 -0
- package/api-doc/{defmath.defmath.json → defmath.json} +6 -46
- package/api-doc/defsave.json +518 -0
- package/api-doc/deftest.json +741 -0
- package/api-doc/{dicebag.dicebag.json → dicebag.json} +19 -1
- package/api-doc/druid.json +249 -1846
- package/api-doc/event.json +1074 -0
- package/api-doc/gooey.json +2050 -0
- package/api-doc/immutable.json +65 -0
- package/api-doc/in.accelerometer.json +75 -1
- package/api-doc/in.button.json +17 -10
- package/api-doc/in.cursor.json +26 -0
- package/api-doc/in.gesture.json +401 -6
- package/api-doc/in.keyboard.json +2 -0
- package/api-doc/in.mapper.json +12 -8
- package/api-doc/in.onscreen.json +61 -5
- package/api-doc/in.state.json +61 -13
- package/api-doc/in.textbox.json +23 -12
- package/api-doc/in.triggers.json +336 -0
- package/api-doc/lang.json +528 -0
- package/api-doc/{event.event.json → log.json} +96 -101
- package/api-doc/metrics.fps.json +61 -2
- package/api-doc/metrics.mem.json +61 -2
- package/api-doc/monarch.monarch.json +249 -7
- package/api-doc/monarch.transitions.easings.json +19 -26
- package/api-doc/monarch.transitions.gui.json +782 -79
- package/api-doc/nakama.engine.defold.json +31 -18
- package/api-doc/{nakama.nakama.json → nakama.json} +4752 -2554
- package/api-doc/nakama.util.log.json +32 -4
- package/api-doc/narrator.json +647 -0
- package/api-doc/openapi/nakama.nakama.json +7680 -0
- package/api-doc/orthographic.camera.json +96 -98
- package/api-doc/orthographic.json +602 -0
- package/api-doc/panthera.json +1183 -0
- package/api-doc/{persist.persist.json → persist.json} +26 -1
- package/api-doc/{platypus.platypus.json → platypus.json} +68 -14
- package/api-doc/proto.json +995 -0
- package/api-doc/{rendy.rendy.json → rendy.json} +46 -99
- package/api-doc/richtext.color.json +77 -0
- package/api-doc/richtext.richtext.json +375 -5
- package/api-doc/saver.saver.json +714 -263
- package/api-doc/saver.storage.json +283 -52
- package/api-doc/squid.json +846 -0
- package/api-doc/{starly.starly.json → starly.json} +1 -1
- package/api-doc/tweener.json +277 -0
- package/api-doc/{yagames.yagames.json → yagames.json} +784 -189
- package/api-doc/{zzfx.api.json → zzfx.json} +9 -9
- package/authored-targets.json +403 -0
- package/generated/{boom.boom.d.ts → boom.d.ts} +14 -0
- package/generated/bridge.d.ts +468 -0
- package/generated/{bzAnim.bzLibrary.d.ts → bzAnim.d.ts} +6 -0
- package/generated/decore.d.ts +37 -37
- package/generated/defcon.d.ts +43 -0
- package/generated/{defmath.defmath.d.ts → defmath.d.ts} +3 -2
- package/generated/defsave.d.ts +144 -0
- package/generated/{dicebag.dicebag.d.ts → dicebag.d.ts} +25 -0
- package/generated/druid.d.ts +198 -502
- package/generated/event.d.ts +318 -0
- package/generated/{gooey.gooey.d.ts → gooey.d.ts} +61 -6
- package/generated/immutable.d.ts +13 -0
- package/generated/in.onscreen.d.ts +5 -0
- package/generated/in.textbox.d.ts +4 -0
- package/generated/lang.d.ts +101 -0
- package/generated/log.d.ts +36 -0
- package/generated/metrics.fps.d.ts +19 -1
- package/generated/metrics.mem.d.ts +19 -1
- package/generated/monarch.monarch.d.ts +44 -1
- package/generated/monarch.transitions.easings.d.ts +0 -1
- package/generated/monarch.transitions.gui.d.ts +96 -12
- package/generated/nakama.d.ts +1158 -0
- package/generated/nakama.engine.defold.d.ts +3 -1
- package/generated/nakama.util.log.d.ts +6 -0
- package/generated/narrator.d.ts +121 -0
- package/generated/openapi/nakama.nakama.d.ts +884 -0
- package/generated/orthographic.camera.d.ts +24 -38
- package/generated/orthographic.d.ts +155 -0
- package/generated/panthera.d.ts +170 -0
- package/generated/{persist.persist.d.ts → persist.d.ts} +3 -0
- package/generated/{platypus.platypus.d.ts → platypus.d.ts} +9 -0
- package/generated/proto.d.ts +146 -0
- package/generated/{rendy.rendy.d.ts → rendy.d.ts} +33 -80
- package/generated/richtext.color.d.ts +15 -0
- package/generated/saver.saver.d.ts +289 -42
- package/generated/saver.storage.d.ts +79 -14
- package/generated/squid.d.ts +127 -0
- package/generated/tweener.d.ts +42 -0
- package/generated/{yagames.yagames.d.ts → yagames.d.ts} +151 -29
- package/generated/{zzfx.api.d.ts → zzfx.d.ts} +7 -0
- package/library-classification.json +0 -227
- package/library-targets.json +1 -284
- package/luals-targets.json +168 -1
- package/openapi-targets.json +17 -0
- package/package.json +9 -88
- package/script-api-targets.json +15 -0
- package/scripts/__snapshots__/parse-luals.test.ts.snap +504 -85
- package/scripts/apply-luals-overrides.ts +95 -0
- package/scripts/authored-parity.ts +718 -0
- package/scripts/emit-library-dts.ts +177 -19
- package/scripts/extract-api-doc.ts +189 -38
- package/scripts/fidelity-floor.ts +178 -0
- package/scripts/import-upstream-docs.ts +125 -0
- package/scripts/lower-api-doc.ts +69 -19
- package/scripts/luals-fidelity.ts +22 -10
- package/scripts/map-luals-types.ts +86 -22
- package/scripts/parse-lua-surface.ts +187 -0
- package/scripts/parse-luals.ts +473 -22
- package/scripts/parse-markdown-api.ts +247 -0
- package/scripts/parse-openapi-api.ts +313 -0
- package/scripts/sync-authored-types.ts +249 -0
- package/scripts/sync-library-types.ts +99 -3
- package/scripts/sync-luals-types.ts +29 -2
- package/scripts/sync-markdown-types.ts +761 -0
- package/scripts/sync-openapi-types.ts +308 -0
- package/scripts/sync-script-api-types.ts +367 -0
- package/api-doc/boom.boom.json +0 -26
- package/api-doc/bzAnim.bzLibrary.json +0 -156
- package/api-doc/defcon.console.json +0 -109
- package/api-doc/defsave.defsave.json +0 -175
- package/api-doc/deftest.deftest.json +0 -71
- package/api-doc/gooey.gooey.json +0 -963
- package/api-doc/immutable.immutable.json +0 -63
- package/api-doc/lang.lang.json +0 -411
- package/api-doc/log.log.json +0 -50
- package/api-doc/narrator.narrator.json +0 -150
- package/api-doc/proto.proto.json +0 -355
- package/api-doc/squid.squid.json +0 -660
- package/api-doc/tweener.tweener.json +0 -419
- package/generated/bridge.bridge.d.ts +0 -533
- package/generated/defcon.console.d.ts +0 -24
- package/generated/defsave.defsave.d.ts +0 -31
- package/generated/event.event.d.ts +0 -54
- package/generated/immutable.immutable.d.ts +0 -13
- package/generated/lang.lang.d.ts +0 -33
- package/generated/log.log.d.ts +0 -40
- package/generated/nakama.nakama.d.ts +0 -594
- package/generated/narrator.narrator.d.ts +0 -66
- package/generated/proto.proto.d.ts +0 -36
- package/generated/squid.squid.d.ts +0 -106
- package/generated/tweener.tweener.d.ts +0 -151
- /package/generated/{deftest.deftest.d.ts → deftest.d.ts} +0 -0
- /package/generated/{starly.starly.d.ts → starly.d.ts} +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { extractApiDoc } from "./extract-api-doc";
|
|
4
|
+
import { type ApiDocElement, importUpstreamDocs } from "./import-upstream-docs";
|
|
5
|
+
import { type LuaMember, parseLuaSurface } from "./parse-lua-surface";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The authored/forked front-end: a fourth `library-types` corpus mode beside the
|
|
9
|
+
* LuaLS (`sync-luals-types.ts`), `.script_api` (`sync-script-api-types.ts`), and
|
|
10
|
+
* markdown (`sync-markdown-types.ts`) lanes. The other three ingest a *non-*`.d.ts`
|
|
11
|
+
* primary source and generate a declaration; this one is for a library whose
|
|
12
|
+
* upstream has no usable structured source, so its `.d.ts` is hand-authored or
|
|
13
|
+
* forked from the retired ts-defold `generated/` golden and vendored under
|
|
14
|
+
* `fixtures/authored/<moduleId>.d.ts`.
|
|
15
|
+
*
|
|
16
|
+
* A vendored authored `.d.ts` is already target-form — a `declare module
|
|
17
|
+
* '<moduleId>'` ambient — so the lane is light: it emits the vendored source
|
|
18
|
+
* verbatim as the `generated/<namespace>.d.ts` golden and runs
|
|
19
|
+
* `extractApiDoc` for the `api-doc/<namespace>.json` the docs-site consumes. The
|
|
20
|
+
* api-doc alone carries one thing the fork does not: where a target vendors its
|
|
21
|
+
* upstream `.lua` and the fork documents a member nowhere, upstream's own LuaDoc
|
|
22
|
+
* summary is lowered in, marked `docSource: "upstream"` (see
|
|
23
|
+
* `import-upstream-docs.ts`). The *emitted* surface is still the vendored authored
|
|
24
|
+
* source untouched, so the go/no-go gate is a
|
|
25
|
+
* forked-vs-generated identity diff (see the golden loop) — emission fidelity.
|
|
26
|
+
* There is no *type* coverage comparison because upstream declares no types; the
|
|
27
|
+
* *surface* is another matter, and a target that vendors its upstream `.lua` under
|
|
28
|
+
* `upstreamLua` is measured on member names and arity by `authored-parity.ts`.
|
|
29
|
+
* Beyond that, a corrected fork's accuracy rests on its manual audit against
|
|
30
|
+
* upstream plus the per-library shape assertions in the sibling test.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Why a target's surface cannot be measured against upstream. A closed set: the
|
|
34
|
+
* alternative to `upstreamLua` has to be a *chosen* category, because free prose
|
|
35
|
+
* would let "nobody looked yet" and "looked, and it is genuinely unmeasurable"
|
|
36
|
+
* read the same in the config.
|
|
37
|
+
*
|
|
38
|
+
* - `unresolved-path` — the `moduleId`-derived path does not resolve at the
|
|
39
|
+
* pinned `ref` (including a repository that no longer exists there).
|
|
40
|
+
* - `no-module-file` — the upstream resolves but declares no single module file
|
|
41
|
+
* this target could be measured against.
|
|
42
|
+
* - `unparseable-shape` — the module file exists but closes in a way
|
|
43
|
+
* `parseLuaSurface` refuses, such as a metatable that could delegate members
|
|
44
|
+
* no column-0 scan would find.
|
|
45
|
+
*/
|
|
46
|
+
export const PARITY_VERDICT_REASONS = [
|
|
47
|
+
"no-module-file",
|
|
48
|
+
"unresolved-path",
|
|
49
|
+
"unparseable-shape",
|
|
50
|
+
] as const;
|
|
51
|
+
|
|
52
|
+
export type ParityVerdictReason = (typeof PARITY_VERDICT_REASONS)[number];
|
|
53
|
+
|
|
54
|
+
export interface ParityVerdict {
|
|
55
|
+
reason: ParityVerdictReason;
|
|
56
|
+
note: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface AuthoredTarget {
|
|
60
|
+
repo: string;
|
|
61
|
+
ref: string;
|
|
62
|
+
// Package-relative path of the vendored authored/forked `.d.ts`
|
|
63
|
+
// (`fixtures/authored/<moduleId>.d.ts`).
|
|
64
|
+
authored: string;
|
|
65
|
+
moduleId: string;
|
|
66
|
+
namespace: string;
|
|
67
|
+
generated: string;
|
|
68
|
+
apiDoc: string;
|
|
69
|
+
// Defaults to `fidelity/<namespace>.json` when omitted. A fork carries no
|
|
70
|
+
// fidelity artifact — there is no primary source to measure against, and the
|
|
71
|
+
// emit is lossless by construction — but the field mirrors the sibling lanes
|
|
72
|
+
// so a future hand-authored target can record one.
|
|
73
|
+
fidelity: string;
|
|
74
|
+
// SPDX-style license id, surfaced by the docs-site provenance block. Optional
|
|
75
|
+
// in the config; defaults to "".
|
|
76
|
+
license: string;
|
|
77
|
+
// Package-relative paths of the vendored upstream `.lua` sources this target's
|
|
78
|
+
// surface is measured against (`authored-parity.ts`). Optional and defaulted to
|
|
79
|
+
// `[]`: a target with no vendored upstream is simply unmeasured, which is why it
|
|
80
|
+
// must not join `REQUIRED_FIELDS`.
|
|
81
|
+
upstreamLua: string[];
|
|
82
|
+
// Why this target vendors no upstream `.lua`. Mutually exclusive with a
|
|
83
|
+
// non-empty `upstreamLua`, and one of the two is mandatory: neither field is
|
|
84
|
+
// required on its own, but the *disjunction* is, so a target cannot land
|
|
85
|
+
// unexamined and read as measured-and-clean. Absent on a measured target.
|
|
86
|
+
parityVerdict?: ParityVerdict;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface AuthoredTargets {
|
|
90
|
+
targets: AuthoredTarget[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const REQUIRED_FIELDS = [
|
|
94
|
+
"repo",
|
|
95
|
+
"ref",
|
|
96
|
+
"authored",
|
|
97
|
+
"moduleId",
|
|
98
|
+
"namespace",
|
|
99
|
+
"generated",
|
|
100
|
+
"apiDoc",
|
|
101
|
+
] as const;
|
|
102
|
+
|
|
103
|
+
/** Validate the parity declaration: exactly one of a non-empty `upstreamLua` and
|
|
104
|
+
* a well-formed `parityVerdict`. Enforced here rather than through
|
|
105
|
+
* `REQUIRED_FIELDS` because neither field is required on its own — the bijection
|
|
106
|
+
* between them is the gate. */
|
|
107
|
+
function validateParityDeclaration(
|
|
108
|
+
label: string,
|
|
109
|
+
upstreamLua: string[],
|
|
110
|
+
verdict: ParityVerdict | undefined,
|
|
111
|
+
): void {
|
|
112
|
+
if (upstreamLua.length > 0 && verdict !== undefined) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`authored-targets.json: entry ${label} declares both upstreamLua and parityVerdict — a measured target carries no verdict.`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (upstreamLua.length === 0 && verdict === undefined) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`authored-targets.json: entry ${label} declares neither upstreamLua nor parityVerdict — vendor its upstream .lua, or record a parityVerdict saying why it cannot be measured.`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (verdict === undefined) return;
|
|
123
|
+
if (!(PARITY_VERDICT_REASONS as readonly string[]).includes(verdict.reason)) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`authored-targets.json: entry ${label} has parityVerdict reason "${verdict.reason}", which is not one of ${PARITY_VERDICT_REASONS.join(", ")}.`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
if (typeof verdict.note !== "string" || verdict.note.trim() === "") {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`authored-targets.json: entry ${label} has a parityVerdict with an empty note — say what was looked at and why it could not be measured.`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read `authored-targets.json`, validate every required field per entry, and fill
|
|
137
|
+
* optional defaults (`fidelity` → `fidelity/<namespace>.json`, `license` → "",
|
|
138
|
+
* `upstreamLua` → []).
|
|
139
|
+
* Throws on the first missing field naming both the field and the offending entry
|
|
140
|
+
* (its `moduleId`, or its index when `moduleId` itself is absent) — the loud-fail
|
|
141
|
+
* discipline `readMarkdownTargets`/`readScriptApiTargets`/`readLualsTargets` use.
|
|
142
|
+
* Also throws when an entry's parity declaration is missing, doubled, or
|
|
143
|
+
* malformed (see `validateParityDeclaration`).
|
|
144
|
+
*/
|
|
145
|
+
export function readAuthoredTargets(packageRoot: string): AuthoredTarget[] {
|
|
146
|
+
const parsed = JSON.parse(readFileSync(join(packageRoot, "authored-targets.json"), "utf8")) as {
|
|
147
|
+
targets: Partial<AuthoredTarget>[];
|
|
148
|
+
};
|
|
149
|
+
return parsed.targets.map((entry, index) => {
|
|
150
|
+
const label = typeof entry.moduleId === "string" ? entry.moduleId : `index ${index}`;
|
|
151
|
+
for (const field of REQUIRED_FIELDS) {
|
|
152
|
+
if (entry[field] === undefined) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`authored-targets.json: entry ${label} is missing required field "${field}".`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const upstreamLua = entry.upstreamLua ?? [];
|
|
159
|
+
validateParityDeclaration(label, upstreamLua, entry.parityVerdict);
|
|
160
|
+
return {
|
|
161
|
+
repo: entry.repo as string,
|
|
162
|
+
ref: entry.ref as string,
|
|
163
|
+
authored: entry.authored as string,
|
|
164
|
+
moduleId: entry.moduleId as string,
|
|
165
|
+
namespace: entry.namespace as string,
|
|
166
|
+
generated: entry.generated as string,
|
|
167
|
+
apiDoc: entry.apiDoc as string,
|
|
168
|
+
fidelity: entry.fidelity ?? `fidelity/${entry.namespace as string}.json`,
|
|
169
|
+
license: entry.license ?? "",
|
|
170
|
+
upstreamLua,
|
|
171
|
+
...(entry.parityVerdict === undefined ? {} : { parityVerdict: entry.parityVerdict }),
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function readAuthoredSource(packageRoot: string, target: AuthoredTarget): string {
|
|
177
|
+
return readFileSync(join(packageRoot, target.authored), "utf8");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The `generated/<namespace>.d.ts` golden, under whatever stem the entry pins —
|
|
182
|
+
* a bare segment or the dotted `moduleId`. The vendored authored
|
|
183
|
+
* `.d.ts` is already a `declare module '<moduleId>'` ambient, so the emit is the
|
|
184
|
+
* source verbatim — the vendored surface passes through unchanged, which is what
|
|
185
|
+
* makes the emission lossless. It says nothing about how that vendored surface
|
|
186
|
+
* compares to upstream.
|
|
187
|
+
*/
|
|
188
|
+
export function emitAuthoredDeclaration(packageRoot: string, target: AuthoredTarget): string {
|
|
189
|
+
return readAuthoredSource(packageRoot, target);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The pinned upstream members by name, the way `authored-parity.ts` builds them:
|
|
193
|
+
* a later definition of a name wins, matching Lua. Empty for a target that vendors
|
|
194
|
+
* no upstream `.lua`, which is then never parsed. */
|
|
195
|
+
function upstreamMembers(packageRoot: string, target: AuthoredTarget): Map<string, LuaMember> {
|
|
196
|
+
const members = new Map<string, LuaMember>();
|
|
197
|
+
for (const relative of target.upstreamLua) {
|
|
198
|
+
for (const member of parseLuaSurface(readFileSync(join(packageRoot, relative), "utf8")).members)
|
|
199
|
+
members.set(member.name, member);
|
|
200
|
+
}
|
|
201
|
+
return members;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* `extractApiDoc` the vendored `.d.ts` under the pinned publish namespace, plus
|
|
206
|
+
* upstream's own LuaDoc for the members the fork documents nowhere, pretty-printed
|
|
207
|
+
* as the `api-doc/<namespace>.json` golden.
|
|
208
|
+
*
|
|
209
|
+
* The api-doc is therefore the fork *plus* imported upstream prose, and it is the
|
|
210
|
+
* only artifact that is: the emitted `generated/<namespace>.d.ts` stays the vendored
|
|
211
|
+
* source verbatim, so the identity diff still proves the emit is lossless. An
|
|
212
|
+
* imported element carries `docSource: "upstream"` through to the docs-site, which
|
|
213
|
+
* marks it rather than presenting it as first-party. See `import-upstream-docs.ts`
|
|
214
|
+
* for the merge rules.
|
|
215
|
+
*/
|
|
216
|
+
export function lowerAuthoredApiDoc(packageRoot: string, target: AuthoredTarget): string {
|
|
217
|
+
// `extractApiDoc` returns `unknown`; the merge needs the element list, and
|
|
218
|
+
// overwriting the existing `elements` key by spread keeps it in place, so the
|
|
219
|
+
// golden's key order is unchanged.
|
|
220
|
+
const doc = extractApiDoc(readAuthoredSource(packageRoot, target), target.namespace) as Record<
|
|
221
|
+
string,
|
|
222
|
+
unknown
|
|
223
|
+
> & { elements: ApiDocElement[] };
|
|
224
|
+
const elements = importUpstreamDocs(doc.elements, upstreamMembers(packageRoot, target));
|
|
225
|
+
return `${JSON.stringify({ ...doc, elements }, null, 2)}\n`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (import.meta.main) {
|
|
229
|
+
const root = join(import.meta.dir, "..");
|
|
230
|
+
const argv = process.argv.slice(2);
|
|
231
|
+
if (argv.includes("--emit")) {
|
|
232
|
+
for (const target of readAuthoredTargets(root)) {
|
|
233
|
+
const contents = emitAuthoredDeclaration(root, target);
|
|
234
|
+
const dest = join(root, target.generated);
|
|
235
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
236
|
+
writeFileSync(dest, contents);
|
|
237
|
+
console.log(`emitted ${target.moduleId} -> ${target.generated}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (argv.includes("--api-doc")) {
|
|
241
|
+
for (const target of readAuthoredTargets(root)) {
|
|
242
|
+
const json = lowerAuthoredApiDoc(root, target);
|
|
243
|
+
const dest = join(root, target.apiDoc);
|
|
244
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
245
|
+
writeFileSync(dest, json);
|
|
246
|
+
console.log(`lowered ${target.moduleId} -> ${target.apiDoc}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -2,6 +2,11 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
import { extractApiDoc } from "./extract-api-doc";
|
|
5
|
+
import { readAuthoredTargets } from "./sync-authored-types";
|
|
6
|
+
import { readLualsTargets } from "./sync-luals-types";
|
|
7
|
+
import { readMarkdownTargets } from "./sync-markdown-types";
|
|
8
|
+
import { readOpenApiTargets } from "./sync-openapi-types";
|
|
9
|
+
import { readScriptApiTargets } from "./sync-script-api-types";
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* ts-defold/library core-type references -> the @defold-typescript/types surface.
|
|
@@ -330,6 +335,65 @@ export function classifyLibraryDirs(
|
|
|
330
335
|
.sort((a, b) => (a.dir < b.dir ? -1 : a.dir > b.dir ? 1 : 0));
|
|
331
336
|
}
|
|
332
337
|
|
|
338
|
+
/**
|
|
339
|
+
* The union every maintained-here lane contributes: authored `moduleId`s, and
|
|
340
|
+
* the `namespace`s of the LuaLS, script_api, OpenAPI and markdown front-ends.
|
|
341
|
+
* Namespaces are matched in both forms because the lanes use both — druid-style
|
|
342
|
+
* libraries register a bare `tweener`, while `defold-saver` registers the dotted
|
|
343
|
+
* `saver.saver` / `saver.storage`.
|
|
344
|
+
*/
|
|
345
|
+
export interface MaintainedHereRegistry {
|
|
346
|
+
moduleIds: ReadonlySet<string>;
|
|
347
|
+
namespaces: ReadonlySet<string>;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The subset of `modules` this repo maintains itself rather than sourcing from
|
|
352
|
+
* ts-defold/library. A live `library-targets.json` row always wins, so a module
|
|
353
|
+
* registered on a maintained-here lane *and* still vendored stays
|
|
354
|
+
* ts-defold-sourced and keeps its dir classified `already-vendored`. The
|
|
355
|
+
* registry is empty today, so no committed module takes that branch; it is what
|
|
356
|
+
* a returning row would take.
|
|
357
|
+
*/
|
|
358
|
+
export function maintainedHereModules(
|
|
359
|
+
modules: readonly string[],
|
|
360
|
+
registry: MaintainedHereRegistry,
|
|
361
|
+
liveModules: ReadonlySet<string>,
|
|
362
|
+
): Set<string> {
|
|
363
|
+
return new Set(
|
|
364
|
+
modules.filter(
|
|
365
|
+
(m) =>
|
|
366
|
+
!liveModules.has(m) &&
|
|
367
|
+
(registry.moduleIds.has(m) ||
|
|
368
|
+
registry.namespaces.has(m) ||
|
|
369
|
+
registry.namespaces.has(m.split(".")[0] as string)),
|
|
370
|
+
),
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The upstream dirs this repo has severed onto a maintained-here lane. Severance
|
|
376
|
+
* deletes a library's `library-targets.json` row, but its dir still exists
|
|
377
|
+
* upstream — so a regen would re-add it under whatever its module shape implies
|
|
378
|
+
* unless it is filtered out first. The registries give an exact derivation with
|
|
379
|
+
* no hand-maintained list: a dir is severed iff it has at least one module and
|
|
380
|
+
* every module is maintained here. A dir mixing maintained-here and live modules
|
|
381
|
+
* is still upstream-sourced and stays classified — `nakama-defold` was the last
|
|
382
|
+
* such dir, and severed whole once its live row went.
|
|
383
|
+
*/
|
|
384
|
+
export function severedDirsFromModules(
|
|
385
|
+
modulesByDir: Map<string, string[]>,
|
|
386
|
+
registry: MaintainedHereRegistry,
|
|
387
|
+
liveModules: ReadonlySet<string>,
|
|
388
|
+
): Set<string> {
|
|
389
|
+
const severed = new Set<string>();
|
|
390
|
+
for (const [dir, modules] of modulesByDir) {
|
|
391
|
+
const maintained = maintainedHereModules(modules, registry, liveModules);
|
|
392
|
+
if (modules.length > 0 && maintained.size === modules.length) severed.add(dir);
|
|
393
|
+
}
|
|
394
|
+
return severed;
|
|
395
|
+
}
|
|
396
|
+
|
|
333
397
|
interface GithubTreeResponse {
|
|
334
398
|
tree?: { path: string }[];
|
|
335
399
|
}
|
|
@@ -376,8 +440,28 @@ export type ListTree = (source: LibrarySource) => Promise<string[]>;
|
|
|
376
440
|
const defaultListTree: ListTree = (source) => githubTreePaths(repoSlug(source.repo), source.commit);
|
|
377
441
|
|
|
378
442
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
443
|
+
* Read every maintained-here registry off the package root and union them.
|
|
444
|
+
* Each reader `readFileSync`s unconditionally and throws on a missing required
|
|
445
|
+
* field — deliberately no absent-file fallback, so a lane added without its
|
|
446
|
+
* fixture fails loudly rather than silently un-severing that lane's dirs.
|
|
447
|
+
*/
|
|
448
|
+
export function readMaintainedHereRegistry(packageRoot: string): MaintainedHereRegistry {
|
|
449
|
+
return {
|
|
450
|
+
moduleIds: new Set(readAuthoredTargets(packageRoot).map((t) => t.moduleId)),
|
|
451
|
+
namespaces: new Set([
|
|
452
|
+
...readLualsTargets(packageRoot).map((t) => t.namespace),
|
|
453
|
+
...readScriptApiTargets(packageRoot).map((t) => t.namespace),
|
|
454
|
+
...readOpenApiTargets(packageRoot).map((t) => t.namespace),
|
|
455
|
+
...readMarkdownTargets(packageRoot).map((t) => t.namespace),
|
|
456
|
+
]),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Enumerate every ts-defold/library dir at the pin, drop the dirs severed onto
|
|
462
|
+
* a maintained-here lane, classify each survivor by its module-name shape, and write
|
|
463
|
+
* `library-classification.json`. Filtering happens here, not in
|
|
464
|
+
* `classifyLibraryDirs`, so that pass stays a pure map-and-sort. The `listTree`
|
|
381
465
|
* seam keeps the pass offline-testable; only the CLI wires the real call, and it
|
|
382
466
|
* stays out of CI (mirrors `--check`). The manifest pins the same `source` as
|
|
383
467
|
* `library-targets.json`.
|
|
@@ -392,9 +476,21 @@ export async function writeClassification(
|
|
|
392
476
|
targets.map((t) => t.path.split("/")[1]).filter((d): d is string => d !== undefined),
|
|
393
477
|
);
|
|
394
478
|
const coveredByGoalDirs = new Set(["defold-lldebugger", "defold-xmath"]);
|
|
479
|
+
const liveModules = new Set(targets.map((t) => t.module));
|
|
480
|
+
|
|
481
|
+
const registry = readMaintainedHereRegistry(packageRoot);
|
|
395
482
|
|
|
396
483
|
const modulesByDir = libraryModulesFromTree(await listTree(source));
|
|
397
|
-
const
|
|
484
|
+
const severedDirs = severedDirsFromModules(modulesByDir, registry, liveModules);
|
|
485
|
+
const dirs = [...modulesByDir]
|
|
486
|
+
// Vendored wins: a stale maintained-here entry can never erase a live ts-defold row.
|
|
487
|
+
.filter(([dir]) => !severedDirs.has(dir) || vendoredDirs.has(dir))
|
|
488
|
+
// A survivor records only its ts-defold-sourced remainder as evidence, so its
|
|
489
|
+
// written shape agrees with its own `modules` array.
|
|
490
|
+
.map(([dir, modules]) => {
|
|
491
|
+
const maintained = maintainedHereModules(modules, registry, liveModules);
|
|
492
|
+
return { dir, modules: modules.filter((m) => !maintained.has(m)) };
|
|
493
|
+
});
|
|
398
494
|
const entries = classifyLibraryDirs(dirs, { vendoredDirs, coveredByGoalDirs });
|
|
399
495
|
writeFileSync(
|
|
400
496
|
join(packageRoot, "library-classification.json"),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { type AnnotationOverrides, applyAnnotationOverrides } from "./apply-luals-overrides";
|
|
4
|
+
import { type ExternalTypeRef, emitLibraryDeclarations } from "./emit-library-dts";
|
|
4
5
|
import { lowerLibraryModel } from "./lower-api-doc";
|
|
5
6
|
import { buildFidelityReport, type FidelityReport } from "./luals-fidelity";
|
|
6
7
|
import { type LibraryModel, mergeLibraryModels, parseLualsSource } from "./parse-luals";
|
|
@@ -17,11 +18,24 @@ export interface LualsTarget {
|
|
|
17
18
|
sourceGlobs: string[];
|
|
18
19
|
moduleId: string;
|
|
19
20
|
namespace: string;
|
|
21
|
+
// Token -> TypeScript type *text*, not an identifier: the mapper emits the value
|
|
22
|
+
// verbatim. The value may name an ambient global that needs no import — a core
|
|
23
|
+
// rename (`Vector3`, `Opaque<"node">`) or a type the Lua stdlib already declares
|
|
24
|
+
// (`debug.FunctionInfo`) — which is what distinguishes it from `externalTypes`.
|
|
20
25
|
typeRenames: Record<string, string>;
|
|
21
26
|
ignore: string[];
|
|
22
27
|
// SPDX-style license id, surfaced by the docs-site provenance block. Optional
|
|
23
28
|
// in the config; the docs-site defaults an absent value to "".
|
|
24
29
|
license?: string;
|
|
30
|
+
// Post-merge corrections applied in `buildTargetModel` for the cases where an
|
|
31
|
+
// upstream annotation the fixtures freeze diverges from the runtime. Optional and
|
|
32
|
+
// loud-failing on an absent key — see `apply-luals-overrides.ts`.
|
|
33
|
+
annotationOverrides?: AnnotationOverrides;
|
|
34
|
+
// Type tokens this library references but another target owns, keyed by the token:
|
|
35
|
+
// the emitter writes a real cross-module `import` for each. A project consuming this
|
|
36
|
+
// library must declare the named upstream dependency too — `resolve` materializes no
|
|
37
|
+
// inter-library graph. See `emit-library-dts.ts`.
|
|
38
|
+
externalTypes?: Record<string, ExternalTypeRef>;
|
|
25
39
|
}
|
|
26
40
|
|
|
27
41
|
export interface LualsTargets {
|
|
@@ -57,6 +71,10 @@ export function readLualsTargets(packageRoot: string): LualsTarget[] {
|
|
|
57
71
|
typeRenames: entry.typeRenames ?? {},
|
|
58
72
|
ignore: entry.ignore ?? [],
|
|
59
73
|
...(entry.license !== undefined ? { license: entry.license } : {}),
|
|
74
|
+
...(entry.annotationOverrides !== undefined
|
|
75
|
+
? { annotationOverrides: entry.annotationOverrides }
|
|
76
|
+
: {}),
|
|
77
|
+
...(entry.externalTypes !== undefined ? { externalTypes: entry.externalTypes } : {}),
|
|
60
78
|
};
|
|
61
79
|
});
|
|
62
80
|
}
|
|
@@ -188,7 +206,13 @@ export function buildTargetModel(
|
|
|
188
206
|
const parsed = new Map<string, LibraryModel>();
|
|
189
207
|
for (const rel of files) parsed.set(rel, parseLualsSource(readFileSync(join(root, rel), "utf8")));
|
|
190
208
|
const merged = mergeLibraryModels([...parsed.values()]);
|
|
191
|
-
|
|
209
|
+
const model: LibraryModel = {
|
|
210
|
+
...merged,
|
|
211
|
+
moduleFunctions: parsed.get(ownFile)?.moduleFunctions ?? [],
|
|
212
|
+
};
|
|
213
|
+
return target.annotationOverrides
|
|
214
|
+
? applyAnnotationOverrides(model, target.annotationOverrides)
|
|
215
|
+
: model;
|
|
192
216
|
}
|
|
193
217
|
|
|
194
218
|
export function buildTargetFidelity(packageRoot: string, target: LualsTarget): FidelityReport {
|
|
@@ -196,6 +220,7 @@ export function buildTargetFidelity(packageRoot: string, target: LualsTarget): F
|
|
|
196
220
|
target.namespace,
|
|
197
221
|
buildTargetModel(packageRoot, target),
|
|
198
222
|
target.typeRenames,
|
|
223
|
+
target.externalTypes,
|
|
199
224
|
);
|
|
200
225
|
}
|
|
201
226
|
|
|
@@ -279,6 +304,7 @@ if (import.meta.main) {
|
|
|
279
304
|
const declarations = emitLibraryDeclarations(model, {
|
|
280
305
|
moduleId: target.moduleId,
|
|
281
306
|
typeRenames: target.typeRenames,
|
|
307
|
+
externalTypes: target.externalTypes,
|
|
282
308
|
});
|
|
283
309
|
const dest = join(root, "generated", `${target.namespace}.d.ts`);
|
|
284
310
|
mkdirSync(dirname(dest), { recursive: true });
|
|
@@ -293,6 +319,7 @@ if (import.meta.main) {
|
|
|
293
319
|
const lowered = lowerLibraryModel(model, {
|
|
294
320
|
namespace: target.namespace,
|
|
295
321
|
typeRenames: target.typeRenames,
|
|
322
|
+
externalTypes: target.externalTypes,
|
|
296
323
|
});
|
|
297
324
|
const dest = join(root, "api-doc", `${target.namespace}.json`);
|
|
298
325
|
mkdirSync(dirname(dest), { recursive: true });
|