@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,308 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import type { FidelityReport } from "./luals-fidelity";
|
|
4
|
+
import { type OpenApiDoc, parseOpenApi } from "./parse-openapi-api";
|
|
5
|
+
import {
|
|
6
|
+
computeScriptApiFidelity,
|
|
7
|
+
type FetchText,
|
|
8
|
+
loadTypeResolver,
|
|
9
|
+
type ScriptApiDoc,
|
|
10
|
+
type TypeResolver,
|
|
11
|
+
} from "./sync-script-api-types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The OpenAPI/proto ingestion front-end: a fifth `library-types` corpus mode
|
|
15
|
+
* beside the LuaLS front-end (`sync-luals-types.ts`), the `.script_api` front-end
|
|
16
|
+
* (`sync-script-api-types.ts`), the markdown front-end (`sync-markdown-types.ts`),
|
|
17
|
+
* and the authored `.d.ts` lane. It reads a library's pinned REST swagger + realtime
|
|
18
|
+
* `.proto` snapshot (`parseOpenApi`) into the shared ref-doc `doc` shape and routes
|
|
19
|
+
* it through the same emitter (`generateModuleDeclaration`) and fidelity machinery.
|
|
20
|
+
*
|
|
21
|
+
* The swagger/proto name no publish alias, so the parsed doc carries bare element
|
|
22
|
+
* names; each target pins the `namespace` this repo publishes under, and the doc is
|
|
23
|
+
* *retargeted* — the bare names are prefixed onto that namespace — before emitting.
|
|
24
|
+
*
|
|
25
|
+
* A structured-source parse is likely **lower coverage** than the hand-written
|
|
26
|
+
* ts-defold `.d.ts` it would replace (a REST swagger cannot describe a Lua client's
|
|
27
|
+
* hand-written helpers or its realtime socket wrappers), so each cutover is gated:
|
|
28
|
+
* `compareFidelityToTsDefold` reports the member surface lost versus the retired
|
|
29
|
+
* ts-defold declaration, and the target's recorded `decision` (`go` / `no-go`) must
|
|
30
|
+
* match. The parser + front-end land as the reusable foundation either way; only
|
|
31
|
+
* the per-library cutover is gated. The gate helpers are shared verbatim with the
|
|
32
|
+
* markdown lane rather than re-implemented.
|
|
33
|
+
*/
|
|
34
|
+
export {
|
|
35
|
+
compareFidelityToTsDefold,
|
|
36
|
+
type FidelityComparison,
|
|
37
|
+
tsDefoldMembers,
|
|
38
|
+
tsDefoldSurface,
|
|
39
|
+
} from "./sync-markdown-types";
|
|
40
|
+
|
|
41
|
+
export interface OpenApiTarget {
|
|
42
|
+
repo: string;
|
|
43
|
+
ref: string;
|
|
44
|
+
swagger: string;
|
|
45
|
+
proto: string;
|
|
46
|
+
moduleId: string;
|
|
47
|
+
namespace: string;
|
|
48
|
+
generated: string;
|
|
49
|
+
apiDoc: string;
|
|
50
|
+
// Defaults to `fidelity/<namespace>.json` when omitted.
|
|
51
|
+
fidelity: string;
|
|
52
|
+
// SPDX-style license id, surfaced by the docs-site provenance block. Optional
|
|
53
|
+
// in the config; defaults to "".
|
|
54
|
+
license: string;
|
|
55
|
+
// The recorded fidelity go/no-go decision for this target. `no-go` keeps the
|
|
56
|
+
// library ts-defold-sourced; `go` cuts it over. Optional until the gate runs.
|
|
57
|
+
decision?: "go" | "no-go";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface OpenApiTargets {
|
|
61
|
+
targets: OpenApiTarget[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const REQUIRED_FIELDS = [
|
|
65
|
+
"repo",
|
|
66
|
+
"ref",
|
|
67
|
+
"swagger",
|
|
68
|
+
"proto",
|
|
69
|
+
"moduleId",
|
|
70
|
+
"namespace",
|
|
71
|
+
"generated",
|
|
72
|
+
"apiDoc",
|
|
73
|
+
] as const;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Read `openapi-targets.json` (or an in-memory override), validate every required
|
|
77
|
+
* field per entry, and fill optional defaults (`fidelity` → `fidelity/<namespace>.json`,
|
|
78
|
+
* `license` → ""). Throws on the first missing field naming both the field and the
|
|
79
|
+
* offending entry (its `moduleId`, or its index when `moduleId` itself is absent) —
|
|
80
|
+
* the loud-fail discipline `readMarkdownTargets`/`readLualsTargets` use. No network.
|
|
81
|
+
*/
|
|
82
|
+
export function readOpenApiTargets(packageRoot: string, rawOverride?: string): OpenApiTarget[] {
|
|
83
|
+
const raw = rawOverride ?? readFileSync(join(packageRoot, "openapi-targets.json"), "utf8");
|
|
84
|
+
const parsed = JSON.parse(raw) as { targets: Partial<OpenApiTarget>[] };
|
|
85
|
+
return parsed.targets.map((entry, index) => {
|
|
86
|
+
const label = typeof entry.moduleId === "string" ? entry.moduleId : `index ${index}`;
|
|
87
|
+
for (const field of REQUIRED_FIELDS) {
|
|
88
|
+
if (entry[field] === undefined) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`openapi-targets.json: entry ${label} is missing required field "${field}".`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
repo: entry.repo as string,
|
|
96
|
+
ref: entry.ref as string,
|
|
97
|
+
swagger: entry.swagger as string,
|
|
98
|
+
proto: entry.proto as string,
|
|
99
|
+
moduleId: entry.moduleId as string,
|
|
100
|
+
namespace: entry.namespace as string,
|
|
101
|
+
generated: entry.generated as string,
|
|
102
|
+
apiDoc: entry.apiDoc as string,
|
|
103
|
+
fidelity: entry.fidelity ?? `fidelity/${entry.namespace as string}.json`,
|
|
104
|
+
license: entry.license ?? "",
|
|
105
|
+
...(entry.decision !== undefined ? { decision: entry.decision } : {}),
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A GitHub repo URL reduced to the bare `<owner>/<repo>` slug. */
|
|
111
|
+
function repoSlug(repo: string): string {
|
|
112
|
+
return repo
|
|
113
|
+
.replace(/^https:\/\/github\.com\//, "")
|
|
114
|
+
.replace(/\.git$/, "")
|
|
115
|
+
.replace(/\/$/, "");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function rawUrl(target: OpenApiTarget, path: string): string {
|
|
119
|
+
return `https://raw.githubusercontent.com/${repoSlug(target.repo)}/${target.ref}/${path}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function swaggerFixturePath(packageRoot: string, target: OpenApiTarget): string {
|
|
123
|
+
return join(packageRoot, "fixtures/openapi", `${target.moduleId}.swagger.json`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function protoFixturePath(packageRoot: string, target: OpenApiTarget): string {
|
|
127
|
+
return join(packageRoot, "fixtures/openapi", `${target.moduleId}.api.proto`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Snapshot the pinned swagger + realtime `.proto` into `fixtures/openapi/` via the
|
|
132
|
+
* raw-content URL. Snapshot only — no parse. The `fetchText` seam keeps the pass
|
|
133
|
+
* offline-testable; only the CLI `--fetch` arm wires the real network.
|
|
134
|
+
*/
|
|
135
|
+
export async function fetchOpenApiFixtures(
|
|
136
|
+
packageRoot: string,
|
|
137
|
+
target: OpenApiTarget,
|
|
138
|
+
seams: { fetchText: FetchText },
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
for (const [path, dest] of [
|
|
141
|
+
[target.swagger, swaggerFixturePath(packageRoot, target)],
|
|
142
|
+
[target.proto, protoFixturePath(packageRoot, target)],
|
|
143
|
+
] as const) {
|
|
144
|
+
const text = await seams.fetchText(rawUrl(target, path));
|
|
145
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
146
|
+
writeFileSync(dest, text);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface RegenModule {
|
|
151
|
+
generateModuleDeclaration: (entry: {
|
|
152
|
+
namespace: string;
|
|
153
|
+
doc: unknown;
|
|
154
|
+
outFile: string;
|
|
155
|
+
importsFrom?: string;
|
|
156
|
+
moduleId?: string;
|
|
157
|
+
}) => { contents: string; dropped: string[] };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// nakama's structured surface resolves to `string`/`number`/`boolean`/`table`
|
|
161
|
+
// only, so the module-wrapped form never emits a core-types handle and this import
|
|
162
|
+
// never reaches the golden; it mirrors the sibling front-ends for the day an
|
|
163
|
+
// openapi target references a branded engine handle.
|
|
164
|
+
const OPENAPI_CORE_TYPES_IMPORT = "../src/core-types";
|
|
165
|
+
|
|
166
|
+
async function loadGenerate(
|
|
167
|
+
packageRoot: string,
|
|
168
|
+
): Promise<RegenModule["generateModuleDeclaration"]> {
|
|
169
|
+
const typesRoot = join(packageRoot, "..", "types");
|
|
170
|
+
const regen = (await import(join(typesRoot, "scripts", "regen.ts"))) as RegenModule;
|
|
171
|
+
return regen.generateModuleDeclaration;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseFixtureDoc(packageRoot: string, target: OpenApiTarget): OpenApiDoc {
|
|
175
|
+
const swagger = readFileSync(swaggerFixturePath(packageRoot, target), "utf8");
|
|
176
|
+
const proto = readFileSync(protoFixturePath(packageRoot, target), "utf8");
|
|
177
|
+
return parseOpenApi(swagger, proto);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Retarget the parsed doc onto the pinned publish namespace: prefix each bare
|
|
182
|
+
* element name with `<namespace>.` and set `info.namespace`. Unlike the markdown
|
|
183
|
+
* lane (which replaces the README's own alias), the swagger/proto names carry no
|
|
184
|
+
* prefix, so retargeting *prepends* rather than rewrites.
|
|
185
|
+
*/
|
|
186
|
+
export function retargetDoc(doc: OpenApiDoc, namespace: string): OpenApiDoc {
|
|
187
|
+
return {
|
|
188
|
+
info: { ...doc.info, namespace },
|
|
189
|
+
elements: doc.elements.map((element) => ({
|
|
190
|
+
...element,
|
|
191
|
+
name: element.name.includes(".") ? element.name : `${namespace}.${element.name}`,
|
|
192
|
+
})),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function retargetedFixtureDoc(packageRoot: string, target: OpenApiTarget): OpenApiDoc {
|
|
197
|
+
return retargetDoc(parseFixtureDoc(packageRoot, target), target.namespace);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* `parseOpenApi` -> retarget -> `generateModuleDeclaration`. Returns an importable
|
|
202
|
+
* module keyed by `moduleId` (`declare module '<moduleId>'`).
|
|
203
|
+
*/
|
|
204
|
+
export async function emitOpenApiDeclaration(
|
|
205
|
+
packageRoot: string,
|
|
206
|
+
target: OpenApiTarget,
|
|
207
|
+
): Promise<string> {
|
|
208
|
+
const generate = await loadGenerate(packageRoot);
|
|
209
|
+
const doc = retargetedFixtureDoc(packageRoot, target);
|
|
210
|
+
const { contents } = generate({
|
|
211
|
+
namespace: target.namespace,
|
|
212
|
+
doc,
|
|
213
|
+
outFile: `${target.moduleId}.d.ts`,
|
|
214
|
+
importsFrom: OPENAPI_CORE_TYPES_IMPORT,
|
|
215
|
+
moduleId: target.moduleId,
|
|
216
|
+
});
|
|
217
|
+
return contents;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** The api-doc golden is the retargeted ref-doc `doc` itself, pretty-printed. */
|
|
221
|
+
export function lowerOpenApiApiDoc(packageRoot: string, target: OpenApiTarget): string {
|
|
222
|
+
const doc = retargetedFixtureDoc(packageRoot, target);
|
|
223
|
+
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The OpenAPI parser maps every swagger/proto type onto a resolvable token, so no
|
|
227
|
+
// token is expected to be lossy. The set stays empty and any unresolved token
|
|
228
|
+
// loud-fails — a brand-new source's unclassified token must not be swallowed as
|
|
229
|
+
// `unknown` at regen time. Mirror of the markdown lane's `KNOWN_LOSSY_TOKENS`.
|
|
230
|
+
const KNOWN_LOSSY_TOKENS = new Set<string>();
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* openapi-scoped wrapper over the shared `computeScriptApiFidelity`. After the
|
|
234
|
+
* report is built, any `unknownToken` outside `KNOWN_LOSSY_TOKENS` loud-fails. The
|
|
235
|
+
* shared `computeScriptApiFidelity` is left untouched so the script_api/luals/markdown
|
|
236
|
+
* goldens stay green.
|
|
237
|
+
*/
|
|
238
|
+
export function computeOpenApiFidelity(
|
|
239
|
+
namespace: string,
|
|
240
|
+
doc: OpenApiDoc,
|
|
241
|
+
resolver: TypeResolver,
|
|
242
|
+
): FidelityReport {
|
|
243
|
+
const report = computeScriptApiFidelity(namespace, doc as unknown as ScriptApiDoc, resolver);
|
|
244
|
+
const unmappable = report.unknownTokens.filter((token) => !KNOWN_LOSSY_TOKENS.has(token));
|
|
245
|
+
if (unmappable.length > 0) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`openapi fidelity [${namespace}]: unmappable type token(s) ${JSON.stringify(unmappable)} — resolve them or add to KNOWN_LOSSY_TOKENS`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return report;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export async function buildOpenApiFidelity(
|
|
254
|
+
packageRoot: string,
|
|
255
|
+
target: OpenApiTarget,
|
|
256
|
+
): Promise<FidelityReport> {
|
|
257
|
+
const resolver = await loadTypeResolver(packageRoot);
|
|
258
|
+
const doc = retargetedFixtureDoc(packageRoot, target);
|
|
259
|
+
return computeOpenApiFidelity(target.namespace, doc, resolver);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const defaultFetchText: FetchText = async (url) => {
|
|
263
|
+
const res = await fetch(url);
|
|
264
|
+
if (!res.ok) {
|
|
265
|
+
throw new Error(`fetch failed: ${url} -> ${res.status} ${res.statusText}`);
|
|
266
|
+
}
|
|
267
|
+
return res.text();
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
if (import.meta.main) {
|
|
271
|
+
const root = join(import.meta.dir, "..");
|
|
272
|
+
const argv = process.argv.slice(2);
|
|
273
|
+
if (argv.includes("--fetch")) {
|
|
274
|
+
for (const target of readOpenApiTargets(root)) {
|
|
275
|
+
await fetchOpenApiFixtures(root, target, { fetchText: defaultFetchText });
|
|
276
|
+
console.log(`snapshotted ${target.moduleId} from ${repoSlug(target.repo)}@${target.ref}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (argv.includes("--emit")) {
|
|
280
|
+
for (const target of readOpenApiTargets(root)) {
|
|
281
|
+
const contents = await emitOpenApiDeclaration(root, target);
|
|
282
|
+
const dest = join(root, target.generated);
|
|
283
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
284
|
+
writeFileSync(dest, contents);
|
|
285
|
+
console.log(`emitted ${target.moduleId} -> ${target.generated}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (argv.includes("--api-doc")) {
|
|
289
|
+
for (const target of readOpenApiTargets(root)) {
|
|
290
|
+
const json = lowerOpenApiApiDoc(root, target);
|
|
291
|
+
const dest = join(root, target.apiDoc);
|
|
292
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
293
|
+
writeFileSync(dest, json);
|
|
294
|
+
console.log(`lowered ${target.moduleId} -> ${target.apiDoc}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (argv.includes("--fidelity")) {
|
|
298
|
+
for (const target of readOpenApiTargets(root)) {
|
|
299
|
+
const report = await buildOpenApiFidelity(root, target);
|
|
300
|
+
const dest = join(root, target.fidelity);
|
|
301
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
302
|
+
writeFileSync(dest, `${JSON.stringify(report, null, 2)}\n`);
|
|
303
|
+
console.log(
|
|
304
|
+
`${target.moduleId}: coverage ${(report.coverage * 100).toFixed(1)}% (${report.unknownFallbacks} unknown, ${report.undocumentedMembers} undocumented)`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import type { FidelityReport } from "./luals-fidelity";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The script_api ingestion front-end: a third `library-types` corpus mode beside
|
|
7
|
+
* the ts-defold codemod (`sync-library-types.ts`) and the LuaLS front-end
|
|
8
|
+
* (`sync-luals-types.ts`). It ingests a library's own committed `.script_api`
|
|
9
|
+
* snapshot and routes it through the *shared ref-doc emitter* — the exact path
|
|
10
|
+
* the four built-in extensions and the resolve-time extension path use — rather
|
|
11
|
+
* than the LuaLS `emitLibraryDeclarations` (which consumes a `LibraryModel`).
|
|
12
|
+
*
|
|
13
|
+
* Each target pins its exact output paths. Like the LuaLS libraries, a migrated
|
|
14
|
+
* script_api library is the sole maintainer of its namespace, so its goldens are
|
|
15
|
+
* named for the target's `namespace` verbatim (`generated/<namespace>.d.ts`,
|
|
16
|
+
* `api-doc/<namespace>.json`, `fidelity/<namespace>.json` — so `bridge` lands at
|
|
17
|
+
* `generated/bridge.d.ts` and `api-doc/bridge.json`) at the canonical roots,
|
|
18
|
+
* rather than the require `moduleId`, keeping the docs tree and file layout
|
|
19
|
+
* uniform with druid.
|
|
20
|
+
*/
|
|
21
|
+
export interface ScriptApiTarget {
|
|
22
|
+
repo: string;
|
|
23
|
+
ref: string;
|
|
24
|
+
scriptApi: string;
|
|
25
|
+
moduleId: string;
|
|
26
|
+
namespace: string;
|
|
27
|
+
generated: string;
|
|
28
|
+
apiDoc: string;
|
|
29
|
+
// Defaults to `fidelity/<namespace>.json` when omitted.
|
|
30
|
+
fidelity: string;
|
|
31
|
+
// SPDX-style license id, surfaced by the docs-site provenance block. Optional
|
|
32
|
+
// in the config; defaults to "".
|
|
33
|
+
license?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ScriptApiTargets {
|
|
37
|
+
targets: ScriptApiTarget[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const REQUIRED_FIELDS = [
|
|
41
|
+
"repo",
|
|
42
|
+
"ref",
|
|
43
|
+
"scriptApi",
|
|
44
|
+
"moduleId",
|
|
45
|
+
"namespace",
|
|
46
|
+
"generated",
|
|
47
|
+
"apiDoc",
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Read `script-api-targets.json`, validate every required field per entry, and
|
|
52
|
+
* fill optional defaults (`fidelity` → `fidelity/<namespace>.json`,
|
|
53
|
+
* `license` → ""). Throws on the first missing field naming both the field and
|
|
54
|
+
* the offending entry (its `moduleId`, or its index when `moduleId` itself is
|
|
55
|
+
* absent) — the loud-fail discipline `readLualsTargets` uses. No network.
|
|
56
|
+
*/
|
|
57
|
+
export function readScriptApiTargets(packageRoot: string): ScriptApiTarget[] {
|
|
58
|
+
const parsed = JSON.parse(readFileSync(join(packageRoot, "script-api-targets.json"), "utf8")) as {
|
|
59
|
+
targets: Partial<ScriptApiTarget>[];
|
|
60
|
+
};
|
|
61
|
+
return parsed.targets.map((entry, index) => {
|
|
62
|
+
const label = typeof entry.moduleId === "string" ? entry.moduleId : `index ${index}`;
|
|
63
|
+
for (const field of REQUIRED_FIELDS) {
|
|
64
|
+
if (entry[field] === undefined) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`script-api-targets.json: entry ${label} is missing required field "${field}".`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const moduleId = entry.moduleId as string;
|
|
71
|
+
return {
|
|
72
|
+
repo: entry.repo as string,
|
|
73
|
+
ref: entry.ref as string,
|
|
74
|
+
scriptApi: entry.scriptApi as string,
|
|
75
|
+
moduleId,
|
|
76
|
+
namespace: entry.namespace as string,
|
|
77
|
+
generated: entry.generated as string,
|
|
78
|
+
apiDoc: entry.apiDoc as string,
|
|
79
|
+
fidelity: entry.fidelity ?? `fidelity/${entry.namespace as string}.json`,
|
|
80
|
+
license: entry.license ?? "",
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Fetch the raw text at a URL. Network seam — mirrors `sync-luals-types.ts`. */
|
|
86
|
+
export type FetchText = (url: string) => Promise<string>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A GitHub repo URL reduced to the bare `<owner>/<repo>` slug used to address
|
|
90
|
+
* raw content. Mirrors `repoSlug` in the LuaLS front-end.
|
|
91
|
+
*/
|
|
92
|
+
function repoSlug(repo: string): string {
|
|
93
|
+
return repo
|
|
94
|
+
.replace(/^https:\/\/github\.com\//, "")
|
|
95
|
+
.replace(/\.git$/, "")
|
|
96
|
+
.replace(/\/$/, "");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function rawUrl(target: ScriptApiTarget): string {
|
|
100
|
+
return `https://raw.githubusercontent.com/${repoSlug(target.repo)}/${target.ref}/${target.scriptApi}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function fixturePath(packageRoot: string, target: ScriptApiTarget): string {
|
|
104
|
+
return join(packageRoot, "fixtures/script-api", `${target.moduleId}.script_api`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Snapshot the pinned `.script_api` into `fixtures/script-api/<moduleId>.script_api`
|
|
109
|
+
* via the raw-content URL. Snapshot only — no codemod. The `fetchText` seam keeps
|
|
110
|
+
* the pass offline-testable; only the CLI `--fetch` arm wires the real network.
|
|
111
|
+
*/
|
|
112
|
+
export async function fetchScriptApiFixture(
|
|
113
|
+
packageRoot: string,
|
|
114
|
+
target: ScriptApiTarget,
|
|
115
|
+
seams: { fetchText: FetchText },
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const text = await seams.fetchText(rawUrl(target));
|
|
118
|
+
const dest = fixturePath(packageRoot, target);
|
|
119
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
120
|
+
writeFileSync(dest, text);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A single ref-doc `doc` type slot — a parameter or a return value. */
|
|
124
|
+
export interface ScriptApiDocSlot {
|
|
125
|
+
types: string[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ScriptApiDocElement {
|
|
129
|
+
type: string;
|
|
130
|
+
name: string;
|
|
131
|
+
description: string;
|
|
132
|
+
parameters: ScriptApiDocSlot[];
|
|
133
|
+
returnvalues: ScriptApiDocSlot[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The core ref-doc JSON shape `scriptApiToFixtureJson` produces. */
|
|
137
|
+
export interface ScriptApiDoc {
|
|
138
|
+
info: { namespace: string };
|
|
139
|
+
elements: ScriptApiDocElement[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Reports whether a single ref-doc type token maps to a real TS type. */
|
|
143
|
+
export interface TypeResolver {
|
|
144
|
+
resolves(token: string): boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface SyncApiDocsModule {
|
|
148
|
+
scriptApiToFixtureJson: (text: string) => string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface RegenModule {
|
|
152
|
+
generateModuleDeclaration: (entry: {
|
|
153
|
+
namespace: string;
|
|
154
|
+
doc: unknown;
|
|
155
|
+
outFile: string;
|
|
156
|
+
importsFrom?: string;
|
|
157
|
+
moduleId?: string;
|
|
158
|
+
}) => { contents: string; dropped: string[] };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface EmitDtsModule {
|
|
162
|
+
recoverCallbackSignature: (token: string) => string | null;
|
|
163
|
+
isKnownDefoldTypeToken: (token: string) => boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
interface TypesModules {
|
|
167
|
+
scriptApiToFixtureJson: (text: string) => string;
|
|
168
|
+
generateModuleDeclaration: RegenModule["generateModuleDeclaration"];
|
|
169
|
+
resolver: TypeResolver;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// bridge references no branded engine handle, so `generateModuleDeclaration`
|
|
173
|
+
// emits no core-types import and this value never reaches the golden; it mirrors
|
|
174
|
+
// the built-in extensions' import for the day a script_api target does reference
|
|
175
|
+
// one.
|
|
176
|
+
const SCRIPT_API_CORE_TYPES_IMPORT = "../src/core-types";
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Load the shared ref-doc emitter and the emitter's own type-mapping surface from
|
|
180
|
+
* the sibling `@defold-typescript/types` package by resolved path, mirroring
|
|
181
|
+
* `extension-emit.ts`'s `loadEmitter`: `scriptApiToFixtureJson` (YAML -> ref-doc
|
|
182
|
+
* JSON) and `generateModuleDeclaration` live in the types package's `scripts/`,
|
|
183
|
+
* `recoverCallbackSignature`/`isKnownDefoldTypeToken` in its `src/`. This is a
|
|
184
|
+
* repo-only build script (never shipped), so the sibling `../types` path is
|
|
185
|
+
* sufficient.
|
|
186
|
+
*
|
|
187
|
+
* Resolution delegates to the emitter's own exported `isKnownDefoldTypeToken` —
|
|
188
|
+
* the predicate the engine-release importer already trusts — rather than
|
|
189
|
+
* re-deriving it here, so fidelity mirrors the emitter's real output. That
|
|
190
|
+
* includes `nil`, which is absent from `DEFOLD_TYPE_MAP` because the emitter
|
|
191
|
+
* lowers it structurally (parameter-side optionality, return-side `undefined`)
|
|
192
|
+
* rather than by lookup.
|
|
193
|
+
*/
|
|
194
|
+
async function loadTypesModules(packageRoot: string): Promise<TypesModules> {
|
|
195
|
+
const typesRoot = join(packageRoot, "..", "types");
|
|
196
|
+
const sync = (await import(join(typesRoot, "scripts", "sync-api-docs.ts"))) as SyncApiDocsModule;
|
|
197
|
+
const regen = (await import(join(typesRoot, "scripts", "regen.ts"))) as RegenModule;
|
|
198
|
+
const emitDts = (await import(join(typesRoot, "src", "emit-dts.ts"))) as EmitDtsModule;
|
|
199
|
+
const resolver: TypeResolver = {
|
|
200
|
+
resolves: (token) => emitDts.isKnownDefoldTypeToken(token),
|
|
201
|
+
};
|
|
202
|
+
return {
|
|
203
|
+
scriptApiToFixtureJson: sync.scriptApiToFixtureJson,
|
|
204
|
+
generateModuleDeclaration: regen.generateModuleDeclaration,
|
|
205
|
+
resolver,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** The emitter's type-mapping predicate, isolated for direct assertion. */
|
|
210
|
+
export async function loadTypeResolver(packageRoot: string): Promise<TypeResolver> {
|
|
211
|
+
return (await loadTypesModules(packageRoot)).resolver;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseFixtureDoc(
|
|
215
|
+
packageRoot: string,
|
|
216
|
+
target: ScriptApiTarget,
|
|
217
|
+
scriptApiToFixtureJson: (text: string) => string,
|
|
218
|
+
): ScriptApiDoc {
|
|
219
|
+
const text = readFileSync(fixturePath(packageRoot, target), "utf8");
|
|
220
|
+
return JSON.parse(scriptApiToFixtureJson(text)) as ScriptApiDoc;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* `.script_api` -> `scriptApiToFixtureJson` -> `generateModuleDeclaration`. Returns
|
|
225
|
+
* an importable module keyed by `moduleId` (`declare module '<moduleId>'`), with
|
|
226
|
+
* one-level nested sub-namespaces intact per the nested-namespace parser slice.
|
|
227
|
+
*/
|
|
228
|
+
export async function emitScriptApiDeclaration(
|
|
229
|
+
packageRoot: string,
|
|
230
|
+
target: ScriptApiTarget,
|
|
231
|
+
): Promise<string> {
|
|
232
|
+
const { scriptApiToFixtureJson, generateModuleDeclaration } = await loadTypesModules(packageRoot);
|
|
233
|
+
const doc = parseFixtureDoc(packageRoot, target, scriptApiToFixtureJson);
|
|
234
|
+
const { contents } = generateModuleDeclaration({
|
|
235
|
+
namespace: target.namespace,
|
|
236
|
+
doc,
|
|
237
|
+
outFile: `${target.moduleId}.d.ts`,
|
|
238
|
+
importsFrom: SCRIPT_API_CORE_TYPES_IMPORT,
|
|
239
|
+
moduleId: target.moduleId,
|
|
240
|
+
});
|
|
241
|
+
return contents;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The api-doc golden is the parsed ref-doc `doc` itself, pretty-printed — the same
|
|
246
|
+
* `{ info, elements }` shape the built-in extensions' `<ns>_doc.json` fixtures
|
|
247
|
+
* feed the docs-site through `parseDefoldApiDoc`, so the shape stays uniform.
|
|
248
|
+
*/
|
|
249
|
+
export async function lowerScriptApiApiDoc(
|
|
250
|
+
packageRoot: string,
|
|
251
|
+
target: ScriptApiTarget,
|
|
252
|
+
): Promise<string> {
|
|
253
|
+
const { scriptApiToFixtureJson } = await loadTypesModules(packageRoot);
|
|
254
|
+
const doc = parseFixtureDoc(packageRoot, target, scriptApiToFixtureJson);
|
|
255
|
+
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function round3(value: number): number {
|
|
259
|
+
return Math.round(value * 1000) / 1000;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Build the `FidelityReport` (same shape as the LuaLS `fidelity/<ns>.json`) over a
|
|
264
|
+
* parsed ref-doc `doc`. Every function element's param/return type tokens are run
|
|
265
|
+
* through `resolver`; a token the emitter cannot map (`resolver.resolves` false)
|
|
266
|
+
* is counted in `unknownFallbacks` and surfaced in `unknownTokens` rather than
|
|
267
|
+
* hidden behind the coverage number. `undocumentedMembers` counts function
|
|
268
|
+
* elements with an empty description. Deterministic; no I/O.
|
|
269
|
+
*/
|
|
270
|
+
export function computeScriptApiFidelity(
|
|
271
|
+
namespace: string,
|
|
272
|
+
doc: ScriptApiDoc,
|
|
273
|
+
resolver: TypeResolver,
|
|
274
|
+
): FidelityReport {
|
|
275
|
+
let totalMembers = 0;
|
|
276
|
+
let totalTypeTokens = 0;
|
|
277
|
+
let unknownFallbacks = 0;
|
|
278
|
+
let undocumentedMembers = 0;
|
|
279
|
+
const unknownTokens = new Set<string>();
|
|
280
|
+
|
|
281
|
+
for (const element of doc.elements) {
|
|
282
|
+
if (element.type !== "FUNCTION") continue;
|
|
283
|
+
totalMembers++;
|
|
284
|
+
if ((element.description ?? "").trim() === "") undocumentedMembers++;
|
|
285
|
+
for (const slot of [...element.parameters, ...element.returnvalues]) {
|
|
286
|
+
for (const token of slot.types) {
|
|
287
|
+
totalTypeTokens++;
|
|
288
|
+
if (!resolver.resolves(token)) {
|
|
289
|
+
unknownFallbacks++;
|
|
290
|
+
unknownTokens.add(token);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const coverage =
|
|
297
|
+
totalTypeTokens === 0
|
|
298
|
+
? 1
|
|
299
|
+
: round3(Math.max(0, Math.min(1, (totalTypeTokens - unknownFallbacks) / totalTypeTokens)));
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
namespace,
|
|
303
|
+
totalMembers,
|
|
304
|
+
totalTypeTokens,
|
|
305
|
+
unknownFallbacks,
|
|
306
|
+
unknownTokens: [...unknownTokens].sort(),
|
|
307
|
+
undocumentedMembers,
|
|
308
|
+
coverage,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function buildScriptApiFidelity(
|
|
313
|
+
packageRoot: string,
|
|
314
|
+
target: ScriptApiTarget,
|
|
315
|
+
): Promise<FidelityReport> {
|
|
316
|
+
const { scriptApiToFixtureJson, resolver } = await loadTypesModules(packageRoot);
|
|
317
|
+
const doc = parseFixtureDoc(packageRoot, target, scriptApiToFixtureJson);
|
|
318
|
+
return computeScriptApiFidelity(target.namespace, doc, resolver);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const defaultFetchText: FetchText = async (url) => {
|
|
322
|
+
const res = await fetch(url);
|
|
323
|
+
if (!res.ok) {
|
|
324
|
+
throw new Error(`fetch failed: ${url} -> ${res.status} ${res.statusText}`);
|
|
325
|
+
}
|
|
326
|
+
return res.text();
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
if (import.meta.main) {
|
|
330
|
+
const root = join(import.meta.dir, "..");
|
|
331
|
+
const argv = process.argv.slice(2);
|
|
332
|
+
if (argv.includes("--fetch")) {
|
|
333
|
+
for (const target of readScriptApiTargets(root)) {
|
|
334
|
+
await fetchScriptApiFixture(root, target, { fetchText: defaultFetchText });
|
|
335
|
+
console.log(`snapshotted ${target.moduleId} from ${repoSlug(target.repo)}@${target.ref}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (argv.includes("--emit")) {
|
|
339
|
+
for (const target of readScriptApiTargets(root)) {
|
|
340
|
+
const contents = await emitScriptApiDeclaration(root, target);
|
|
341
|
+
const dest = join(root, target.generated);
|
|
342
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
343
|
+
writeFileSync(dest, contents);
|
|
344
|
+
console.log(`emitted ${target.moduleId} -> ${target.generated}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (argv.includes("--api-doc")) {
|
|
348
|
+
for (const target of readScriptApiTargets(root)) {
|
|
349
|
+
const json = await lowerScriptApiApiDoc(root, target);
|
|
350
|
+
const dest = join(root, target.apiDoc);
|
|
351
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
352
|
+
writeFileSync(dest, json);
|
|
353
|
+
console.log(`lowered ${target.moduleId} -> ${target.apiDoc}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (argv.includes("--fidelity")) {
|
|
357
|
+
for (const target of readScriptApiTargets(root)) {
|
|
358
|
+
const report = await buildScriptApiFidelity(root, target);
|
|
359
|
+
const dest = join(root, target.fidelity);
|
|
360
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
361
|
+
writeFileSync(dest, `${JSON.stringify(report, null, 2)}\n`);
|
|
362
|
+
console.log(
|
|
363
|
+
`${target.moduleId}: coverage ${(report.coverage * 100).toFixed(1)}% (${report.unknownFallbacks} unknown, ${report.undocumentedMembers} undocumented)`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
package/api-doc/boom.boom.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"info": {
|
|
3
|
-
"namespace": "boom.boom",
|
|
4
|
-
"brief": "Boom is a game framework built on top of Defold.",
|
|
5
|
-
"description": "Boom is a game framework built on top of Defold."
|
|
6
|
-
},
|
|
7
|
-
"elements": [
|
|
8
|
-
{
|
|
9
|
-
"type": "FUNCTION",
|
|
10
|
-
"name": "boom",
|
|
11
|
-
"brief": "Start a boom game. Call this from your own game script",
|
|
12
|
-
"description": "Start a boom game. Call this from your own game script",
|
|
13
|
-
"parameters": [
|
|
14
|
-
{
|
|
15
|
-
"name": "game",
|
|
16
|
-
"doc": "Game loop function",
|
|
17
|
-
"types": [
|
|
18
|
-
"() => void"
|
|
19
|
-
],
|
|
20
|
-
"is_optional": "False"
|
|
21
|
-
}
|
|
22
|
-
],
|
|
23
|
-
"returnvalues": []
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
}
|