@defold-typescript/types 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api-availability.json +333 -306
- package/api-signatures.json +47 -46
- package/api-targets.json +66 -2
- package/generated/collectionproxy.d.ts +22 -0
- package/generated/editor-vm/http.d.ts +56 -0
- package/generated/editor-vm/image.d.ts +48 -0
- package/generated/editor-vm/json.d.ts +10 -0
- package/generated/editor-vm/localization.d.ts +42 -0
- package/generated/editor-vm/tilemap_tiles.d.ts +80 -0
- package/generated/editor-vm/zip.d.ts +10 -0
- package/generated/editor-vm/zlib.d.ts +24 -0
- package/generated/editor.d.ts +1111 -0
- package/generated/go.d.ts +22 -22
- package/generated/gui.d.ts +35 -6
- package/generated/kinds/editor-script.d.ts +14 -0
- package/generated/kinds/gui-script.d.ts +1 -0
- package/generated/kinds/render-script.d.ts +1 -0
- package/generated/kinds/script.d.ts +1 -0
- package/generated/material.d.ts +4 -0
- package/generated/versions/defold-1.12.4/go.d.ts +22 -22
- package/generated/versions/defold-1.12.4/gui.d.ts +43 -43
- package/index.d.ts +9 -0
- package/package.json +9 -1
- package/scripts/import-defold-release.ts +10 -0
- package/scripts/materialize-version.ts +54 -13
- package/scripts/regen.ts +425 -42
- package/scripts/sync-api-docs.ts +86 -9
- package/src/api-availability.ts +0 -0
- package/src/core-types.ts +15 -1
- package/src/editor-overloads.d.ts +33 -0
- package/src/editor-vm-globals.d.ts +200 -0
- package/src/editor-vm-types.ts +44 -0
- package/src/editor.ts +117 -16
- package/src/emit-dts.ts +298 -79
- package/src/engine-globals.d.ts +2 -2
- package/src/go-overloads.d.ts +6 -6
- package/src/index.ts +15 -0
- package/src/msg-overloads.d.ts +3 -3
- package/src/scene-addresses.d.ts +62 -0
- package/src/url-parameters.ts +174 -0
- package/url-parameters.json +285 -0
package/scripts/regen.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
3
|
import messagesDoc from "../fixtures/messages_doc.json" with { type: "json" };
|
|
4
4
|
import { type ApiModule, parseDefoldApiDoc } from "../src/api-doc";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
defaultMapType,
|
|
7
|
+
emitDeclarations,
|
|
8
|
+
emitSymbolSignatures,
|
|
9
|
+
type SymbolSignature,
|
|
10
|
+
} from "../src/emit-dts";
|
|
6
11
|
import {
|
|
7
12
|
applyMessageDeprecations,
|
|
8
13
|
emitBuiltinMessages,
|
|
@@ -10,6 +15,7 @@ import {
|
|
|
10
15
|
} from "../src/emit-messages";
|
|
11
16
|
import type { TranslationStore } from "../src/example-store";
|
|
12
17
|
import { wrapAsAmbientGlobal, wrapAsModule } from "../src/publish-dts";
|
|
18
|
+
import type { UrlParameterTable } from "../src/url-parameters";
|
|
13
19
|
import {
|
|
14
20
|
type DocSourceProvenance,
|
|
15
21
|
type DownloadRefDoc,
|
|
@@ -28,6 +34,16 @@ export interface ApiTargetModule {
|
|
|
28
34
|
readonly skipFunctions?: readonly string[];
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
// An editor-VM document declared by a target. It carries two things a runtime
|
|
38
|
+
// module never needs: its own `importsFrom` (the `editor-vm/` subdirectory sits
|
|
39
|
+
// a level below the runtime surface, so the target's `coreTypesImport` does not
|
|
40
|
+
// reach) and a named `mapType` selector, since the editor VM is the only surface
|
|
41
|
+
// with handle tokens no runtime namespace uses.
|
|
42
|
+
export interface ApiTargetEditorModule extends ApiTargetModule {
|
|
43
|
+
readonly importsFrom?: string;
|
|
44
|
+
readonly mapType?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
// A target sourced from a resolved ref-doc zip (resolved on demand, never
|
|
32
48
|
// pre-baked) versus the committed-fixture default (`null`).
|
|
33
49
|
export type ApiTargetSource = { readonly kind: "ref-doc"; readonly version: string } | null;
|
|
@@ -40,6 +56,11 @@ export interface ApiTarget {
|
|
|
40
56
|
readonly coreTypesImport: string;
|
|
41
57
|
readonly source?: ApiTargetSource;
|
|
42
58
|
readonly modules: readonly ApiTargetModule[];
|
|
59
|
+
// The editor-scripting documents this target ships, or absent when it ships
|
|
60
|
+
// none. Absence is declared, never inferred from a missing file, so a fixture
|
|
61
|
+
// deleted by accident fails loudly instead of silently degrading a pinned
|
|
62
|
+
// project to the default target's editor surface.
|
|
63
|
+
readonly editorModules?: readonly ApiTargetEditorModule[];
|
|
43
64
|
// Docs-only Lua stdlib pages (no generated `.d.ts`): vendored fixtures the
|
|
44
65
|
// docs-site pages under the "Lua standard library" category. Never read by
|
|
45
66
|
// regen/MODULE_MANIFEST; surfaced here so the registry stays type-honest.
|
|
@@ -49,7 +70,10 @@ export interface ApiTarget {
|
|
|
49
70
|
const REGISTRY_PATH = resolve(import.meta.dir, "..", "api-targets.json");
|
|
50
71
|
const PACKAGE_ROOT = resolve(import.meta.dir, "..");
|
|
51
72
|
|
|
52
|
-
export function loadApiTargets(
|
|
73
|
+
export function loadApiTargets(
|
|
74
|
+
registryPath: string = REGISTRY_PATH,
|
|
75
|
+
packageRoot: string = resolve(registryPath, ".."),
|
|
76
|
+
): ApiTarget[] {
|
|
53
77
|
const { targets } = JSON.parse(readFileSync(registryPath, "utf8")) as { targets: ApiTarget[] };
|
|
54
78
|
const defaults = targets.filter((t) => t.default === true);
|
|
55
79
|
if (defaults.length !== 1) {
|
|
@@ -57,6 +81,16 @@ export function loadApiTargets(registryPath: string = REGISTRY_PATH): ApiTarget[
|
|
|
57
81
|
`api-targets.json: expected exactly one default target, found ${defaults.length}`,
|
|
58
82
|
);
|
|
59
83
|
}
|
|
84
|
+
for (const target of targets) {
|
|
85
|
+
for (const module of target.editorModules ?? []) {
|
|
86
|
+
const path = resolve(packageRoot, target.fixturesDir, module.fixture);
|
|
87
|
+
if (!existsSync(path)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`api-targets.json: target "${target.id}" editor module "${module.namespace}" fixture not found: ${path}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
60
94
|
return targets;
|
|
61
95
|
}
|
|
62
96
|
|
|
@@ -88,10 +122,18 @@ export interface ModuleManifestEntry {
|
|
|
88
122
|
readonly namespace: string;
|
|
89
123
|
readonly doc: unknown;
|
|
90
124
|
readonly outFile: string;
|
|
125
|
+
// Each item drops one member of any element kind — a FUNCTION or a VARIABLE —
|
|
126
|
+
// by an exact stripped local (`get`), or — when it ends in `.` — a segment
|
|
127
|
+
// prefix dropping everything beneath it (`ui.`). The field keeps its historic
|
|
128
|
+
// name; its reach is not limited to functions.
|
|
91
129
|
readonly skipFunctions?: readonly string[];
|
|
92
130
|
readonly importsFrom?: string;
|
|
93
131
|
readonly moduleId?: string;
|
|
94
132
|
readonly sourceProvenance?: DocSourceProvenance;
|
|
133
|
+
// Overrides the shared token -> TS type mapping for this entry alone. Reserved
|
|
134
|
+
// for tokens no runtime namespace uses, so `DEFOLD_TYPE_MAP` keeps describing
|
|
135
|
+
// only the runtime surface.
|
|
136
|
+
readonly mapType?: (token: string) => string;
|
|
95
137
|
}
|
|
96
138
|
|
|
97
139
|
export interface ResolveTargetOptions {
|
|
@@ -157,6 +199,152 @@ const FIDELITY_BASELINE_TARGET = DEFAULT_TARGET;
|
|
|
157
199
|
export const FIDELITY_BASELINE_MANIFEST: readonly ModuleManifestEntry[] =
|
|
158
200
|
loadTargetModules(FIDELITY_BASELINE_TARGET);
|
|
159
201
|
|
|
202
|
+
// Handle tokens the editor VM alone exposes. `transaction_step[` is what
|
|
203
|
+
// upstream literally emits for `transaction_step[]` — the ref-doc's own type
|
|
204
|
+
// string loses the closing bracket — so it is repaired here, per entry, rather
|
|
205
|
+
// than in the parser: no runtime namespace carries it, and normalizing a
|
|
206
|
+
// truncated bracket globally would silently reinterpret any future token of that
|
|
207
|
+
// shape. The doc's other truncated token, `string[`, is deliberately left to map
|
|
208
|
+
// to `unknown`: it covers both a plain path list and `create_resources`' mixed
|
|
209
|
+
// path/[path, content] entries, so `string[]` would be wrong at one of its two
|
|
210
|
+
// slots.
|
|
211
|
+
const EDITOR_TYPE_MAP: Readonly<Record<string, string>> = {
|
|
212
|
+
// `editor.command` — the token's only consumer today — is skipped in favour of
|
|
213
|
+
// the hand-authored overload file, which returns the same `Opaque<"command">`.
|
|
214
|
+
// The entry stays so a future `command`-returning function maps identically.
|
|
215
|
+
command: 'Opaque<"command">',
|
|
216
|
+
// The handle every `editor.ui.*` builder returns and `editor.ui.show_dialog`
|
|
217
|
+
// consumes. Branding it keeps the whole component chain one nominal type
|
|
218
|
+
// rather than letting each end fall to the default `unknown` mapping.
|
|
219
|
+
component: 'Opaque<"component">',
|
|
220
|
+
transaction_step: 'Opaque<"transaction_step">',
|
|
221
|
+
"transaction_step[": 'Opaque<"transaction_step">[]',
|
|
222
|
+
// The handle every `localization` function returns: a userdata whose only
|
|
223
|
+
// documented use is being stringified or nested into another pattern.
|
|
224
|
+
message: 'Opaque<"message">',
|
|
225
|
+
// Repaired per entry for the same reason as `transaction_step[` above. Unlike
|
|
226
|
+
// `string[`, this token means one thing everywhere it appears — a list of
|
|
227
|
+
// values to render — so `unknown[]` is sound at all three of its slots.
|
|
228
|
+
"any[": "unknown[]",
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
function mapEditorType(token: string): string {
|
|
232
|
+
return EDITOR_TYPE_MAP[token] ?? defaultMapType(token);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// The named type maps an `editorModules` entry may select. A closed set, so an
|
|
236
|
+
// unknown selector fails loudly instead of silently falling back to the runtime
|
|
237
|
+
// token mapping and emitting `unknown` for every editor handle.
|
|
238
|
+
const NAMED_TYPE_MAPS: Readonly<Record<string, (token: string) => string>> = {
|
|
239
|
+
editor: mapEditorType,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// The subdirectory the namespace-shaped libraries the editor VM exposes
|
|
243
|
+
// alongside `editor` emit into, rather than beside the runtime modules, for a
|
|
244
|
+
// hard reason: `tsconfig.json` includes the flat glob `generated/*.d.ts`, so an
|
|
245
|
+
// editor `http.d.ts` there would share a program with the runtime one and
|
|
246
|
+
// declare `namespace http` twice. A subdirectory keeps them out of that glob,
|
|
247
|
+
// the same escape `generated/versions/` and `generated/kinds/` already use.
|
|
248
|
+
// Because that boundary is forced, it also discriminates the two halves of a
|
|
249
|
+
// target's declaration: the `editor` namespace a kind index names directly, and
|
|
250
|
+
// the VM libraries it must import by explicit path.
|
|
251
|
+
export const EDITOR_VM_SUBDIR = "editor-vm/";
|
|
252
|
+
|
|
253
|
+
export function isEditorVmModule(module: { readonly outFile: string }): boolean {
|
|
254
|
+
return module.outFile.startsWith(EDITOR_VM_SUBDIR);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function oneLevelDeeper(importPath: string): string {
|
|
258
|
+
return importPath.startsWith("./") ? `../${importPath.slice(2)}` : `../${importPath}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// The editor-scripting surface a target declares. Vendored and emitted through
|
|
262
|
+
// the same pipeline as MODULE_MANIFEST but deliberately separate from it:
|
|
263
|
+
// MODULE_MANIFEST drives every runtime kind's universal imports, the per-version
|
|
264
|
+
// targets and the published API artifacts, none of which describe the editor VM.
|
|
265
|
+
// Reached only through the `editor-script` kind index.
|
|
266
|
+
//
|
|
267
|
+
// The `skipFunctions` rules a target declares in `api-targets.json` withhold
|
|
268
|
+
// members for reasons the registry data alone cannot state. Rules are local
|
|
269
|
+
// names — the `<namespace>.` prefix is stripped before matching — and they
|
|
270
|
+
// withhold VARIABLEs as well as FUNCTIONs.
|
|
271
|
+
//
|
|
272
|
+
// On the `editor` entry, the editor VM's own `http`/`json`/`zip`/`zlib`/
|
|
273
|
+
// `pprint`/`localization`/`tilemap.tiles` sit in that same upstream document
|
|
274
|
+
// under their own top-level namespaces. They are emitted from their own
|
|
275
|
+
// per-namespace fixtures instead, and skipped on `editor` so that entry cannot
|
|
276
|
+
// misname them as `editor.*` — `pprint`, a flat identifier, would otherwise land
|
|
277
|
+
// as `editor.pprint`. `command` is the one rule there that is not a dropped
|
|
278
|
+
// namespace prefix: the hand-authored `src/editor-overloads.d.ts` supplies
|
|
279
|
+
// `editor.command` with a generic signature that couples a command's opts bag to
|
|
280
|
+
// its own query, which the emitter cannot express.
|
|
281
|
+
//
|
|
282
|
+
// On a VM entry, the withheld members are hand-authored in
|
|
283
|
+
// `src/editor-vm-globals.d.ts` instead. The functions are ones whose vendored
|
|
284
|
+
// signature the emitter cannot render soundly, both causes being the fixture's
|
|
285
|
+
// positional model being a lossy description of the Lua function its own
|
|
286
|
+
// `examples` block calls: an optional parameter sitting *before* a required one
|
|
287
|
+
// (TypeScript cannot mark a middle parameter `?`, so the emit renders it
|
|
288
|
+
// `T | undefined` and rejects every documented short form), and an empty
|
|
289
|
+
// `returnvalues` on a function upstream's own prose says returns a value.
|
|
290
|
+
//
|
|
291
|
+
// The constant tables are expressible now that the nested pass reaches
|
|
292
|
+
// variables, but a VARIABLE carries no `types`, so the emit would be `unknown` —
|
|
293
|
+
// and `ZipPackOptions.method?: string` rejects that. Their brief is the literal
|
|
294
|
+
// value, which looks like a string-literal type until you notice upstream's own
|
|
295
|
+
// `zip.ON_CONFLICT.OVERWRITE` reads `"skip"`, so no type is derived from it.
|
|
296
|
+
// The hand-authored declarations stay authoritative and these stay withheld.
|
|
297
|
+
export function loadTargetEditorModules(
|
|
298
|
+
target: ApiTarget,
|
|
299
|
+
packageRoot: string = PACKAGE_ROOT,
|
|
300
|
+
): ModuleManifestEntry[] {
|
|
301
|
+
return (target.editorModules ?? []).map((module) => {
|
|
302
|
+
const path = resolve(packageRoot, target.fixturesDir, module.fixture);
|
|
303
|
+
let raw: string;
|
|
304
|
+
try {
|
|
305
|
+
raw = readFileSync(path, "utf8");
|
|
306
|
+
} catch {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`api-targets.json: target "${target.id}" editor module "${module.namespace}" fixture not found: ${path}`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (module.mapType !== undefined && NAMED_TYPE_MAPS[module.mapType] === undefined) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`api-targets.json: target "${target.id}" editor module "${module.namespace}": unknown mapType "${module.mapType}"`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const entry: ModuleManifestEntry = {
|
|
317
|
+
namespace: module.namespace,
|
|
318
|
+
doc: JSON.parse(raw),
|
|
319
|
+
outFile: module.outFile,
|
|
320
|
+
// A VM module sits one directory below the rest of the surface, so it
|
|
321
|
+
// cannot ride the target's own `coreTypesImport` unchanged. Deriving the
|
|
322
|
+
// deeper form keeps a relocated surface (a materialized `.defold-types/`
|
|
323
|
+
// copy rewrites `coreTypesImport` to `./core-types`) resolving too.
|
|
324
|
+
importsFrom:
|
|
325
|
+
module.importsFrom ??
|
|
326
|
+
(isEditorVmModule(module)
|
|
327
|
+
? oneLevelDeeper(target.coreTypesImport)
|
|
328
|
+
: target.coreTypesImport),
|
|
329
|
+
...(module.skipFunctions ? { skipFunctions: module.skipFunctions } : {}),
|
|
330
|
+
...(module.mapType ? { mapType: NAMED_TYPE_MAPS[module.mapType] } : {}),
|
|
331
|
+
};
|
|
332
|
+
return entry;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const DEFAULT_EDITOR_MODULES = loadTargetEditorModules(DEFAULT_TARGET);
|
|
337
|
+
|
|
338
|
+
export const EDITOR_MODULE_MANIFEST: readonly ModuleManifestEntry[] = DEFAULT_EDITOR_MODULES.filter(
|
|
339
|
+
(entry) => !isEditorVmModule(entry),
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
export const EDITOR_VM_MODULE_MANIFEST: readonly ModuleManifestEntry[] =
|
|
343
|
+
DEFAULT_EDITOR_MODULES.filter(isEditorVmModule);
|
|
344
|
+
|
|
345
|
+
export const EDITOR_SKIP_FUNCTIONS: readonly string[] =
|
|
346
|
+
EDITOR_MODULE_MANIFEST.find((entry) => entry.namespace === "editor")?.skipFunctions ?? [];
|
|
347
|
+
|
|
160
348
|
export interface MessagesManifestEntry {
|
|
161
349
|
readonly doc: unknown;
|
|
162
350
|
readonly outFile: string;
|
|
@@ -194,15 +382,34 @@ export function collectConstantFqns(
|
|
|
194
382
|
return fqns;
|
|
195
383
|
}
|
|
196
384
|
|
|
385
|
+
const URL_PARAMETERS_PATH = resolve(PACKAGE_ROOT, "url-parameters.json");
|
|
386
|
+
|
|
387
|
+
// The committed classification table, read once. Both the `.d.ts` emit and the
|
|
388
|
+
// authoritative-signature emit default to it, so the two surfaces cannot
|
|
389
|
+
// disagree about which slots address the scene graph.
|
|
390
|
+
export function loadUrlParameters(path: string = URL_PARAMETERS_PATH): UrlParameterTable {
|
|
391
|
+
return JSON.parse(readFileSync(path, "utf8")) as UrlParameterTable;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let urlParametersCache: UrlParameterTable | null = null;
|
|
395
|
+
|
|
396
|
+
function committedUrlParameters(): UrlParameterTable {
|
|
397
|
+
urlParametersCache ??= loadUrlParameters();
|
|
398
|
+
return urlParametersCache;
|
|
399
|
+
}
|
|
400
|
+
|
|
197
401
|
export interface GenerateOptions {
|
|
198
402
|
knownConstantFqns?: ReadonlySet<string>;
|
|
199
403
|
translations?: TranslationStore;
|
|
404
|
+
urlParameters?: UrlParameterTable;
|
|
200
405
|
}
|
|
201
406
|
|
|
202
407
|
interface PreparedGeneratedModule {
|
|
203
408
|
module: ApiModule;
|
|
204
409
|
knownConstantFqns: ReadonlySet<string>;
|
|
205
410
|
translations: TranslationStore;
|
|
411
|
+
urlParameters: UrlParameterTable;
|
|
412
|
+
mapType: ((token: string) => string) | undefined;
|
|
206
413
|
dropped: string[];
|
|
207
414
|
}
|
|
208
415
|
|
|
@@ -217,29 +424,42 @@ function prepareGeneratedModule(
|
|
|
217
424
|
const module = parseDefoldApiDoc(entry.doc);
|
|
218
425
|
const prefix = `${module.namespace}.`;
|
|
219
426
|
const dropped: string[] = [];
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
427
|
+
const rules = entry.skipFunctions ?? [];
|
|
428
|
+
const exact = new Set(rules.filter((rule) => !rule.endsWith(".")));
|
|
429
|
+
const segments = rules.filter((rule) => rule.endsWith("."));
|
|
430
|
+
const withheld = (name: string): boolean => {
|
|
431
|
+
const local = name.startsWith(prefix) ? name.slice(prefix.length) : name;
|
|
432
|
+
if (!exact.has(local) && !segments.some((segment) => local.startsWith(segment))) return false;
|
|
433
|
+
dropped.push(name);
|
|
227
434
|
return true;
|
|
228
|
-
}
|
|
435
|
+
};
|
|
436
|
+
module.functions = module.functions.filter((fn) => !withheld(fn.name));
|
|
437
|
+
module.variables = module.variables.filter((v) => !withheld(v.name));
|
|
229
438
|
const knownConstantFqns = options?.knownConstantFqns ?? collectConstantFqns();
|
|
230
439
|
const translations = options?.translations ?? loadTranslations();
|
|
231
|
-
|
|
440
|
+
const urlParameters = options?.urlParameters ?? committedUrlParameters();
|
|
441
|
+
return {
|
|
442
|
+
module,
|
|
443
|
+
knownConstantFqns,
|
|
444
|
+
translations,
|
|
445
|
+
urlParameters,
|
|
446
|
+
mapType: entry.mapType,
|
|
447
|
+
dropped,
|
|
448
|
+
};
|
|
232
449
|
}
|
|
233
450
|
|
|
234
451
|
export function generateModuleDeclaration(
|
|
235
452
|
entry: ModuleManifestEntry,
|
|
236
453
|
options?: GenerateOptions,
|
|
237
454
|
): GenerateResult {
|
|
238
|
-
const { module, knownConstantFqns, translations, dropped } =
|
|
239
|
-
entry,
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
455
|
+
const { module, knownConstantFqns, translations, urlParameters, mapType, dropped } =
|
|
456
|
+
prepareGeneratedModule(entry, options);
|
|
457
|
+
const emitted = emitDeclarations(module, {
|
|
458
|
+
knownConstantFqns,
|
|
459
|
+
translations,
|
|
460
|
+
urlParameters,
|
|
461
|
+
...(mapType ? { mapType } : {}),
|
|
462
|
+
});
|
|
243
463
|
const importsFrom = entry.importsFrom ?? "../src/core-types";
|
|
244
464
|
const contents = entry.moduleId
|
|
245
465
|
? wrapAsModule({ namespace: module.namespace, emitted, importsFrom, moduleId: entry.moduleId })
|
|
@@ -255,23 +475,50 @@ export function generateModuleSignatures(
|
|
|
255
475
|
entry: ModuleManifestEntry,
|
|
256
476
|
options?: GenerateOptions,
|
|
257
477
|
): SymbolSignature[] {
|
|
258
|
-
const { module, knownConstantFqns } = prepareGeneratedModule(
|
|
259
|
-
|
|
478
|
+
const { module, knownConstantFqns, urlParameters, mapType } = prepareGeneratedModule(
|
|
479
|
+
entry,
|
|
480
|
+
options,
|
|
481
|
+
);
|
|
482
|
+
return emitSymbolSignatures(module, {
|
|
483
|
+
knownConstantFqns,
|
|
484
|
+
urlParameters,
|
|
485
|
+
...(mapType ? { mapType } : {}),
|
|
486
|
+
});
|
|
260
487
|
}
|
|
261
488
|
|
|
262
489
|
export interface VersionedModuleManifestEntry extends ModuleManifestEntry {
|
|
263
490
|
readonly versionId: string;
|
|
491
|
+
// Set on an entry derived from the target's `editorModules`. The editor
|
|
492
|
+
// surface rides the same per-version emit as the runtime modules but never the
|
|
493
|
+
// version's aggregate index — importing it there would drag the editor VM into
|
|
494
|
+
// every runtime program pinned to that version.
|
|
495
|
+
readonly editor?: boolean;
|
|
264
496
|
}
|
|
265
497
|
|
|
266
498
|
// Committed generation covers only filesystem-fixture targets (source == null).
|
|
267
499
|
// ref-doc targets are resolved on the fly and never pre-baked, so they are
|
|
268
500
|
// excluded from the committed regen loop and the byte-drift guards.
|
|
501
|
+
export function versionedModuleManifest(
|
|
502
|
+
targets: readonly ApiTarget[],
|
|
503
|
+
packageRoot: string = PACKAGE_ROOT,
|
|
504
|
+
): VersionedModuleManifestEntry[] {
|
|
505
|
+
return targets
|
|
506
|
+
.filter((target) => target.default !== true && (target.source ?? null) == null)
|
|
507
|
+
.flatMap((target) => [
|
|
508
|
+
...loadTargetModules(target, packageRoot).map((entry) => ({
|
|
509
|
+
...entry,
|
|
510
|
+
versionId: target.id,
|
|
511
|
+
})),
|
|
512
|
+
...loadTargetEditorModules(target, packageRoot).map((entry) => ({
|
|
513
|
+
...entry,
|
|
514
|
+
versionId: target.id,
|
|
515
|
+
editor: true,
|
|
516
|
+
})),
|
|
517
|
+
]);
|
|
518
|
+
}
|
|
519
|
+
|
|
269
520
|
export const VERSIONED_MODULE_MANIFEST: readonly VersionedModuleManifestEntry[] =
|
|
270
|
-
API_TARGETS
|
|
271
|
-
(target) => target.default !== true && (target.source ?? null) == null,
|
|
272
|
-
).flatMap((target) =>
|
|
273
|
-
loadTargetModules(target).map((entry) => ({ ...entry, versionId: target.id })),
|
|
274
|
-
);
|
|
521
|
+
versionedModuleManifest(API_TARGETS);
|
|
275
522
|
|
|
276
523
|
export const RESTRICTED_NAMESPACES: Readonly<Record<string, string>> = {
|
|
277
524
|
gui: "gui_script",
|
|
@@ -280,9 +527,18 @@ export const RESTRICTED_NAMESPACES: Readonly<Record<string, string>> = {
|
|
|
280
527
|
|
|
281
528
|
// The Lua standard library rides every per-kind subpath the same as the full
|
|
282
529
|
// entrypoint. Triple-slash directives must precede the first statement, so they
|
|
283
|
-
// lead the generated kind index.
|
|
284
|
-
|
|
285
|
-
|
|
530
|
+
// lead the generated kind index. The two lines select independently: the game
|
|
531
|
+
// runtime is LuaJIT, but the editor VM is plain Lua 5.1 and has no `bit`.
|
|
532
|
+
const LUA_51_REFERENCE = '/// <reference types="lua-types/5.1" />\n';
|
|
533
|
+
const LUA_JIT_ONLY_REFERENCE = '/// <reference types="lua-types/special/jit-only" />\n';
|
|
534
|
+
export const LUA_STDLIB_REFERENCES = `${LUA_51_REFERENCE}${LUA_JIT_ONLY_REFERENCE}`;
|
|
535
|
+
|
|
536
|
+
// The stdlib references a kind's own VM earns. Shared with the materialized
|
|
537
|
+
// renderer so a surface generated on the fly makes the same LuaJIT call the
|
|
538
|
+
// committed emit does.
|
|
539
|
+
export function kindStdlibReferences(entry: KindManifestEntry): string {
|
|
540
|
+
return `${LUA_51_REFERENCE}${entry.jit === false ? "" : LUA_JIT_ONLY_REFERENCE}`;
|
|
541
|
+
}
|
|
286
542
|
|
|
287
543
|
const UNIVERSAL_EXTRA_IMPORTS: readonly string[] = [
|
|
288
544
|
"../builtin-messages",
|
|
@@ -290,33 +546,109 @@ const UNIVERSAL_EXTRA_IMPORTS: readonly string[] = [
|
|
|
290
546
|
"../../src/msg-overloads",
|
|
291
547
|
"../../src/message-guard",
|
|
292
548
|
"../../src/window-event-guard",
|
|
549
|
+
"../../src/scene-addresses",
|
|
293
550
|
"../../src/go-overloads",
|
|
294
551
|
"../../src/vmath-overloads",
|
|
295
552
|
];
|
|
296
553
|
|
|
554
|
+
const DEFAULT_FACTORY_MODULE = "../../src/lifecycle";
|
|
555
|
+
|
|
297
556
|
export interface KindManifestEntry {
|
|
298
557
|
readonly kind: string;
|
|
299
558
|
readonly restricted?: string;
|
|
300
559
|
readonly factory: string;
|
|
560
|
+
// Replaces the universal import set with exactly these generated modules. A
|
|
561
|
+
// kind that names one is disjoint from the runtime surface, not a narrowing
|
|
562
|
+
// of it, so it takes none of the universal extras either.
|
|
563
|
+
readonly only?: readonly string[];
|
|
564
|
+
// Hand-authored ambient modules the kind imports on top of `only`. A kind
|
|
565
|
+
// naming `only` takes none of `UNIVERSAL_EXTRA_IMPORTS`, so it cannot ride
|
|
566
|
+
// that set to reach an overload file.
|
|
567
|
+
readonly extraModules?: readonly string[];
|
|
568
|
+
// Extra value exports the factory module contributes to the kind subpath.
|
|
569
|
+
readonly extraExports?: readonly string[];
|
|
570
|
+
// Extra type-only exports the factory module contributes to the kind subpath.
|
|
571
|
+
readonly extraTypeExports?: readonly string[];
|
|
572
|
+
// Where the factory (and, when emitted, the script-property helper types) is
|
|
573
|
+
// re-exported from. Defaults to the runtime lifecycle module.
|
|
574
|
+
readonly factoryFrom?: string;
|
|
575
|
+
// Whether the kind has script properties at all. Editor scripts do not.
|
|
576
|
+
readonly propertyTypes?: boolean;
|
|
577
|
+
// Whether the kind's VM is LuaJIT. The editor runs plain Lua 5.1.
|
|
578
|
+
readonly jit?: boolean;
|
|
301
579
|
}
|
|
302
580
|
|
|
303
581
|
export const KIND_MODULE_MANIFEST: readonly KindManifestEntry[] = [
|
|
304
582
|
{ kind: "script", factory: "defineScript" },
|
|
305
583
|
{ kind: "gui-script", restricted: "gui", factory: "defineGuiScript" },
|
|
306
584
|
{ kind: "render-script", restricted: "render", factory: "defineRenderScript" },
|
|
585
|
+
{
|
|
586
|
+
kind: "editor-script",
|
|
587
|
+
// Empty on purpose: the emitted namespaces come from the declaring target's
|
|
588
|
+
// `editorModules` (see `editorKindModules`), and the empty array is what
|
|
589
|
+
// marks the kind as disjoint from the runtime surface rather than a
|
|
590
|
+
// narrowing of it.
|
|
591
|
+
only: [],
|
|
592
|
+
extraModules: ["../../src/editor-overloads", "../../src/editor-vm-globals"],
|
|
593
|
+
factory: "defineEditorScript",
|
|
594
|
+
extraExports: ["defineEditorCommand"],
|
|
595
|
+
extraTypeExports: ["EditorCommandQuery", "EditorNode"],
|
|
596
|
+
factoryFrom: "../../src/editor",
|
|
597
|
+
propertyTypes: false,
|
|
598
|
+
jit: false,
|
|
599
|
+
},
|
|
307
600
|
];
|
|
308
601
|
|
|
309
|
-
|
|
602
|
+
// The kinds every materialized *versioned* surface carries, whatever the target.
|
|
603
|
+
// An `only` kind is built from the target's own `editorModules`, so it has a
|
|
604
|
+
// versioned form only for a target that declares an editor document — see
|
|
605
|
+
// `targetKindManifest`, which is what a per-target emit resolves through.
|
|
606
|
+
export const RUNTIME_KIND_MANIFEST: readonly KindManifestEntry[] = KIND_MODULE_MANIFEST.filter(
|
|
607
|
+
(entry) => entry.only === undefined,
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
// The emitted modules an editor kind index imports, read from the declaring
|
|
611
|
+
// target rather than from a list beside the kind entry, so a pinned surface
|
|
612
|
+
// names its own editor document instead of the default target's. The plain
|
|
613
|
+
// namespace leads and the VM libraries follow by explicit path — `only` holds
|
|
614
|
+
// namespaces and would build `../http` (the runtime module) and
|
|
615
|
+
// `../tilemap.tiles` (not a path) for those.
|
|
616
|
+
function editorKindModules(target: ApiTarget): string[] {
|
|
617
|
+
const declared = target.editorModules ?? [];
|
|
618
|
+
if (declared.length === 0) {
|
|
619
|
+
throw new Error(`api-targets.json: target "${target.id}" declares no editor document`);
|
|
620
|
+
}
|
|
621
|
+
return [
|
|
622
|
+
...declared.filter((m) => !isEditorVmModule(m)),
|
|
623
|
+
...declared.filter(isEditorVmModule),
|
|
624
|
+
].map((m) => `../${m.outFile.replace(/\.d\.ts$/, "")}`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export function generateKindIndex(kind: string, target: ApiTarget = DEFAULT_TARGET): string {
|
|
310
628
|
const entry = KIND_MODULE_MANIFEST.find((e) => e.kind === kind);
|
|
311
629
|
if (!entry) throw new Error(`unknown script kind: ${kind}`);
|
|
630
|
+
const srcPrefix = targetSrcPrefix(target);
|
|
312
631
|
const universalNamespaces = MODULE_MANIFEST.filter(
|
|
313
632
|
(m) => !Object.hasOwn(RESTRICTED_NAMESPACES, m.namespace),
|
|
314
633
|
).map((m) => `../${m.outFile.replace(/\.d\.ts$/, "")}`);
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
634
|
+
const modules = (
|
|
635
|
+
entry.only === undefined
|
|
636
|
+
? [...new Set([...universalNamespaces.sort(), ...[...UNIVERSAL_EXTRA_IMPORTS].sort()])]
|
|
637
|
+
: [...editorKindModules(target), ...(entry.extraModules ?? [])]
|
|
638
|
+
).map((path) => retargetSrcPath(path, srcPrefix));
|
|
639
|
+
const lines = modules.map((path) => `import "${path}";`);
|
|
318
640
|
if (entry.restricted) lines.push(`import "../${entry.restricted}";`);
|
|
319
|
-
|
|
641
|
+
const references = kindStdlibReferences(entry);
|
|
642
|
+
const from = retargetSrcPath(entry.factoryFrom ?? DEFAULT_FACTORY_MODULE, srcPrefix);
|
|
643
|
+
const values = [entry.factory, ...(entry.extraExports ?? [])].join(", ");
|
|
644
|
+
const typeExports = entry.extraTypeExports?.length
|
|
645
|
+
? `\nexport type { ${entry.extraTypeExports.join(", ")} } from "${from}";`
|
|
646
|
+
: "";
|
|
647
|
+
const properties =
|
|
648
|
+
entry.propertyTypes === false
|
|
649
|
+
? ""
|
|
650
|
+
: `\nexport type { ScriptProperties, ScriptProperty } from "${from}";`;
|
|
651
|
+
return `${references}${lines.join("\n")}\n\nexport { ${values} } from "${from}";${typeExports}${properties}\n`;
|
|
320
652
|
}
|
|
321
653
|
|
|
322
654
|
export function generateVersionIndex(
|
|
@@ -324,7 +656,7 @@ export function generateVersionIndex(
|
|
|
324
656
|
manifest: readonly VersionedModuleManifestEntry[] = VERSIONED_MODULE_MANIFEST,
|
|
325
657
|
): string {
|
|
326
658
|
const imports = manifest
|
|
327
|
-
.filter((entry) => entry.versionId === versionId)
|
|
659
|
+
.filter((entry) => entry.versionId === versionId && entry.editor !== true)
|
|
328
660
|
.map((entry) => entry.outFile.replace(/\.d\.ts$/, ""))
|
|
329
661
|
.sort()
|
|
330
662
|
.map((module) => `import "./${module}";`)
|
|
@@ -332,9 +664,40 @@ export function generateVersionIndex(
|
|
|
332
664
|
return `${imports}\n\nexport {};\n`;
|
|
333
665
|
}
|
|
334
666
|
|
|
667
|
+
// The `src/`-relative prefix a kind index living in `<generatedDir>/kinds/` needs
|
|
668
|
+
// to reach the package's hand-authored modules. The target's own
|
|
669
|
+
// `coreTypesImport` already measures its generated dir's depth; the kind index
|
|
670
|
+
// sits one level below it.
|
|
671
|
+
function targetSrcPrefix(target: ApiTarget): string {
|
|
672
|
+
return `../${target.coreTypesImport.slice(0, -"core-types".length)}`;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const DEFAULT_SRC_PREFIX = "../../src/";
|
|
676
|
+
|
|
677
|
+
function retargetSrcPath(modulePath: string, srcPrefix: string): string {
|
|
678
|
+
return modulePath.startsWith(DEFAULT_SRC_PREFIX)
|
|
679
|
+
? `${srcPrefix}${modulePath.slice(DEFAULT_SRC_PREFIX.length)}`
|
|
680
|
+
: modulePath;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// The kinds a materialized surface for `target` can carry: the runtime trio
|
|
684
|
+
// always, plus the editor kind when — and only when — the target declares an
|
|
685
|
+
// editor document of its own.
|
|
686
|
+
export function targetKindManifest(target: ApiTarget): readonly KindManifestEntry[] {
|
|
687
|
+
if ((target.editorModules?.length ?? 0) === 0) {
|
|
688
|
+
return RUNTIME_KIND_MANIFEST;
|
|
689
|
+
}
|
|
690
|
+
return [...RUNTIME_KIND_MANIFEST, ...KIND_MODULE_MANIFEST.filter((e) => e.only !== undefined)];
|
|
691
|
+
}
|
|
692
|
+
|
|
335
693
|
if (import.meta.main) {
|
|
336
694
|
const generated = resolve(import.meta.dir, "..", "generated");
|
|
337
|
-
|
|
695
|
+
mkdirSync(resolve(generated, "editor-vm"), { recursive: true });
|
|
696
|
+
for (const entry of [
|
|
697
|
+
...MODULE_MANIFEST,
|
|
698
|
+
...EDITOR_MODULE_MANIFEST,
|
|
699
|
+
...EDITOR_VM_MODULE_MANIFEST,
|
|
700
|
+
]) {
|
|
338
701
|
const { contents, dropped } = generateModuleDeclaration(entry);
|
|
339
702
|
if (dropped.length > 0) {
|
|
340
703
|
console.log(`note: dropped skipped member(s) from ${entry.namespace}: ${dropped.join(", ")}`);
|
|
@@ -347,7 +710,17 @@ if (import.meta.main) {
|
|
|
347
710
|
writeFileSync(messagesOut, generateBuiltinMessagesDeclaration(MESSAGES_MANIFEST));
|
|
348
711
|
console.log(`wrote ${messagesOut}`);
|
|
349
712
|
|
|
350
|
-
|
|
713
|
+
// The target's own `generatedDir` is where a version lands, so a declaring
|
|
714
|
+
// target's editor surface (and its `editor-vm/` subdirectory) rides the same
|
|
715
|
+
// destination rule as its runtime modules rather than a second convention.
|
|
716
|
+
const versionedTargets = API_TARGETS.filter(
|
|
717
|
+
(target) => target.default !== true && (target.source ?? null) == null,
|
|
718
|
+
);
|
|
719
|
+
const versionDirOf = (versionId: string): string => {
|
|
720
|
+
const target = versionedTargets.find((t) => t.id === versionId);
|
|
721
|
+
if (!target) throw new Error(`no committed target for version ${versionId}`);
|
|
722
|
+
return resolve(PACKAGE_ROOT, target.generatedDir);
|
|
723
|
+
};
|
|
351
724
|
for (const entry of VERSIONED_MODULE_MANIFEST) {
|
|
352
725
|
const { contents, dropped } = generateModuleDeclaration(entry);
|
|
353
726
|
if (dropped.length > 0) {
|
|
@@ -355,16 +728,26 @@ if (import.meta.main) {
|
|
|
355
728
|
`note: dropped skipped member(s) from ${entry.versionId}/${entry.namespace}: ${dropped.join(", ")}`,
|
|
356
729
|
);
|
|
357
730
|
}
|
|
358
|
-
const
|
|
359
|
-
mkdirSync(
|
|
360
|
-
const out = resolve(versionDir, entry.outFile);
|
|
731
|
+
const out = resolve(versionDirOf(entry.versionId), entry.outFile);
|
|
732
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
361
733
|
writeFileSync(out, contents);
|
|
362
734
|
console.log(`wrote ${out}`);
|
|
363
735
|
}
|
|
364
|
-
for (const
|
|
365
|
-
const indexOut = resolve(
|
|
366
|
-
|
|
736
|
+
for (const target of versionedTargets) {
|
|
737
|
+
const indexOut = resolve(versionDirOf(target.id), "index.d.ts");
|
|
738
|
+
mkdirSync(dirname(indexOut), { recursive: true });
|
|
739
|
+
writeFileSync(indexOut, generateVersionIndex(target.id));
|
|
367
740
|
console.log(`wrote ${indexOut}`);
|
|
741
|
+
|
|
742
|
+
const editorKinds = targetKindManifest(target).filter((entry) => entry.only !== undefined);
|
|
743
|
+
if (editorKinds.length === 0) continue;
|
|
744
|
+
const versionKindsDir = resolve(versionDirOf(target.id), "kinds");
|
|
745
|
+
mkdirSync(versionKindsDir, { recursive: true });
|
|
746
|
+
for (const entry of editorKinds) {
|
|
747
|
+
const out = resolve(versionKindsDir, `${entry.kind}.d.ts`);
|
|
748
|
+
writeFileSync(out, generateKindIndex(entry.kind, target));
|
|
749
|
+
console.log(`wrote ${out}`);
|
|
750
|
+
}
|
|
368
751
|
}
|
|
369
752
|
|
|
370
753
|
const kindsDir = resolve(generated, "kinds");
|