@defold-typescript/transpiler 0.29.0 → 0.31.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/dist/animation-sources.d.ts +6 -0
- package/dist/{sprite-animation-index.d.ts → component-animation-index.d.ts} +4 -4
- package/dist/config-key-index.d.ts +1 -0
- package/dist/gui-flipbook-index.d.ts +8 -0
- package/dist/gui-node-index.d.ts +21 -0
- package/dist/index.d.ts +21 -8
- package/dist/index.js +1317 -289
- package/dist/library-dependencies.d.ts +47 -0
- package/dist/scene-address-declaration.d.ts +14 -0
- package/dist/scene-collection-roles.d.ts +30 -0
- package/dist/scene-component-index.d.ts +20 -0
- package/dist/scene-documents.d.ts +21 -0
- package/dist/scene-naming-context.d.ts +66 -0
- package/dist/scene-object-path-index.d.ts +4 -1
- package/dist/url-address-literals.d.ts +10 -0
- package/dist/url-address-slots.d.ts +3 -1
- package/dist/url-cross-world-address.d.ts +41 -0
- package/dist/url-fragment-reachability.d.ts +3 -0
- package/package.json +2 -2
- package/src/animation-sources.ts +90 -0
- package/src/component-animation-index.ts +399 -0
- package/src/config-key-index.ts +28 -2
- package/src/gui-flipbook-index.ts +91 -0
- package/src/gui-node-index.ts +42 -4
- package/src/index.ts +66 -10
- package/src/library-dependencies.ts +337 -0
- package/src/scene-address-declaration.ts +74 -0
- package/src/scene-collection-roles.ts +267 -0
- package/src/scene-component-index.ts +74 -6
- package/src/scene-documents.ts +186 -0
- package/src/scene-naming-context.ts +130 -0
- package/src/scene-object-path-index.ts +172 -33
- package/src/url-address-literals.ts +115 -0
- package/src/url-address-slots.ts +23 -2
- package/src/url-cross-world-address.ts +109 -0
- package/src/url-fragment-reachability.ts +72 -93
- package/src/sprite-animation-index.ts +0 -241
package/dist/index.js
CHANGED
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
// src/config-key-index.ts
|
|
2
|
-
function sectionNameOf(line) {
|
|
3
|
-
const match = line.trim().match(/^\[(.+)\]$/);
|
|
4
|
-
return match?.[1];
|
|
5
|
-
}
|
|
6
|
-
function buildConfigKeyIndex(gameProjectText) {
|
|
7
|
-
const keys = new Set;
|
|
8
|
-
let section;
|
|
9
|
-
for (const line of gameProjectText.split(`
|
|
10
|
-
`)) {
|
|
11
|
-
const header = sectionNameOf(line);
|
|
12
|
-
if (header !== undefined) {
|
|
13
|
-
section = header;
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (section === undefined)
|
|
17
|
-
continue;
|
|
18
|
-
const separator = line.indexOf("=");
|
|
19
|
-
if (separator === -1)
|
|
20
|
-
continue;
|
|
21
|
-
const key = line.slice(0, separator).trim();
|
|
22
|
-
if (key === "")
|
|
23
|
-
continue;
|
|
24
|
-
keys.add(`${section}.${key}`);
|
|
25
|
-
}
|
|
26
|
-
return keys;
|
|
27
|
-
}
|
|
28
1
|
// src/scene-text-format.ts
|
|
29
2
|
class SceneTextFormatError extends Error {
|
|
30
3
|
line;
|
|
@@ -183,19 +156,404 @@ function parseSceneTextFormat(text) {
|
|
|
183
156
|
return parseBody(false, 1);
|
|
184
157
|
}
|
|
185
158
|
|
|
186
|
-
// src/
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
159
|
+
// src/animation-sources.ts
|
|
160
|
+
function resourceKey(resource) {
|
|
161
|
+
return resource.startsWith("/") ? resource.slice(1) : resource;
|
|
162
|
+
}
|
|
163
|
+
function normalizeDocumentKey(path) {
|
|
164
|
+
return path.replaceAll("\\", "/");
|
|
165
|
+
}
|
|
166
|
+
var SPRITE_SUFFIX = ".sprite";
|
|
167
|
+
var MODEL_SUFFIX = ".model";
|
|
168
|
+
var ANIMATION_SET_SUFFIX = ".animationset";
|
|
169
|
+
var HOP_ONLY_SUFFIXES = [SPRITE_SUFFIX, MODEL_SUFFIX, ANIMATION_SET_SUFFIX];
|
|
170
|
+
function childrenOf(message, name) {
|
|
171
|
+
return message.messages.get(name) ?? [];
|
|
172
|
+
}
|
|
173
|
+
function declaredAnimations(document) {
|
|
174
|
+
const ids = new Set;
|
|
175
|
+
for (const animation of childrenOf(document, "animations")) {
|
|
176
|
+
for (const id of animation.fields.get("id") ?? []) {
|
|
177
|
+
if (id !== "")
|
|
178
|
+
ids.add(id);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return ids;
|
|
182
|
+
}
|
|
183
|
+
function readAnimationSources(assets, unresolved) {
|
|
184
|
+
const animationsByTileSet = new Map;
|
|
185
|
+
for (const [path, text] of assets) {
|
|
186
|
+
const displayPath = normalizeDocumentKey(path);
|
|
187
|
+
if (HOP_ONLY_SUFFIXES.some((suffix) => displayPath.endsWith(suffix)))
|
|
188
|
+
continue;
|
|
189
|
+
let document;
|
|
190
|
+
try {
|
|
191
|
+
document = parseSceneTextFormat(text);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (!(error instanceof SceneTextFormatError))
|
|
194
|
+
throw error;
|
|
195
|
+
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
animationsByTileSet.set(displayPath, declaredAnimations(document));
|
|
199
|
+
}
|
|
200
|
+
return animationsByTileSet;
|
|
201
|
+
}
|
|
202
|
+
function animationSetEntries(document) {
|
|
203
|
+
const entries = [];
|
|
204
|
+
for (const entry of childrenOf(document, "animations")) {
|
|
205
|
+
for (const animation of entry.fields.get("animation") ?? []) {
|
|
206
|
+
if (animation !== "")
|
|
207
|
+
entries.push(animation);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return entries;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/component-animation-index.ts
|
|
214
|
+
var SCRIPT_SUFFIX = ".script";
|
|
215
|
+
var SPRITE_SUFFIX2 = ".sprite";
|
|
216
|
+
var MODEL_SUFFIX2 = ".model";
|
|
217
|
+
var ANIMATION_SET_SUFFIX2 = ".animationset";
|
|
218
|
+
var MESH_SUFFIXES = [".gltf", ".glb"];
|
|
219
|
+
function firstField(message, name) {
|
|
220
|
+
return (message.fields.get(name) ?? [])[0];
|
|
221
|
+
}
|
|
222
|
+
function childrenOf2(message, name) {
|
|
223
|
+
return message.messages.get(name) ?? [];
|
|
224
|
+
}
|
|
225
|
+
function basename(resource) {
|
|
226
|
+
const file = resource.slice(resource.lastIndexOf("/") + 1);
|
|
227
|
+
const dot = file.lastIndexOf(".");
|
|
228
|
+
return dot === -1 ? file : file.slice(0, dot);
|
|
229
|
+
}
|
|
230
|
+
function readAssets(assets, unresolved) {
|
|
231
|
+
const animationsByTileSet = readAnimationSources(assets, unresolved);
|
|
232
|
+
const tileSetBySprite = new Map;
|
|
233
|
+
const modelDocuments = new Set;
|
|
234
|
+
const animationSourceByModel = new Map;
|
|
235
|
+
const entriesByAnimationSet = new Map;
|
|
236
|
+
for (const [path, text] of assets) {
|
|
237
|
+
const displayPath = normalizeDocumentKey(path);
|
|
238
|
+
const isSprite = displayPath.endsWith(SPRITE_SUFFIX2);
|
|
239
|
+
const isModel = displayPath.endsWith(MODEL_SUFFIX2);
|
|
240
|
+
const isAnimationSet = displayPath.endsWith(ANIMATION_SET_SUFFIX2);
|
|
241
|
+
if (!isSprite && !isModel && !isAnimationSet)
|
|
242
|
+
continue;
|
|
243
|
+
let document;
|
|
244
|
+
try {
|
|
245
|
+
document = parseSceneTextFormat(text);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (!(error instanceof SceneTextFormatError))
|
|
248
|
+
throw error;
|
|
249
|
+
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (isSprite) {
|
|
253
|
+
const tileSet = firstField(document, "tile_set");
|
|
254
|
+
if (tileSet !== undefined)
|
|
255
|
+
tileSetBySprite.set(displayPath, resourceKey(tileSet));
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (isModel) {
|
|
259
|
+
modelDocuments.add(displayPath);
|
|
260
|
+
const animations = firstField(document, "animations");
|
|
261
|
+
if (animations !== undefined)
|
|
262
|
+
animationSourceByModel.set(displayPath, animations);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
entriesByAnimationSet.set(displayPath, animationSetEntries(document));
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
animationsByTileSet,
|
|
269
|
+
tileSetBySprite,
|
|
270
|
+
modelDocuments,
|
|
271
|
+
animationSourceByModel,
|
|
272
|
+
entriesByAnimationSet
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function resolveAnimationSet(input) {
|
|
276
|
+
const { path, prefix, stack, assets, unresolved } = input;
|
|
277
|
+
const entries = assets.entriesByAnimationSet.get(path);
|
|
278
|
+
if (entries === undefined)
|
|
190
279
|
return;
|
|
191
|
-
|
|
192
|
-
|
|
280
|
+
stack.add(path);
|
|
281
|
+
try {
|
|
282
|
+
const ids = new Map;
|
|
283
|
+
for (const entry of entries) {
|
|
284
|
+
const key = resourceKey(entry);
|
|
285
|
+
if (MESH_SUFFIXES.some((suffix) => key.endsWith(suffix))) {
|
|
286
|
+
ids.set(prefix + basename(key), path);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (!key.endsWith(ANIMATION_SET_SUFFIX2)) {
|
|
290
|
+
unresolved.push(`${path}: the entry ${entry} is not a .gltf, .glb or .animationset, so it declares no animation id`);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (!assets.entriesByAnimationSet.has(key)) {
|
|
294
|
+
unresolved.push(`${path}: the entry ${entry} is not among the project's animation set documents`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (stack.has(key)) {
|
|
298
|
+
unresolved.push(`${path}: the entry ${entry} re-enters an animation set already being read, so the chain is cyclic`);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const nested = resolveAnimationSet({
|
|
302
|
+
path: key,
|
|
303
|
+
prefix: `${prefix + basename(key)}/`,
|
|
304
|
+
stack,
|
|
305
|
+
assets,
|
|
306
|
+
unresolved
|
|
307
|
+
});
|
|
308
|
+
if (nested === undefined)
|
|
309
|
+
return;
|
|
310
|
+
for (const [id, declaring] of nested)
|
|
311
|
+
if (!ids.has(id))
|
|
312
|
+
ids.set(id, declaring);
|
|
313
|
+
}
|
|
314
|
+
return ids;
|
|
315
|
+
} finally {
|
|
316
|
+
stack.delete(path);
|
|
317
|
+
}
|
|
193
318
|
}
|
|
194
|
-
function
|
|
319
|
+
function componentAnimationSources(object, displayPath, assets, unresolved) {
|
|
320
|
+
const sources = new Map;
|
|
321
|
+
function claimModel(id, animations) {
|
|
322
|
+
if (!animations.endsWith(ANIMATION_SET_SUFFIX2)) {
|
|
323
|
+
unresolved.push(`${displayPath}: the model component "${id}" names ${animations} rather than an animation set, so none of its animation ids are established`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
sources.set(id, { kind: "model", path: resourceKey(animations) });
|
|
327
|
+
}
|
|
328
|
+
for (const embedded of childrenOf2(object, "embedded_components")) {
|
|
329
|
+
const type = firstField(embedded, "type");
|
|
330
|
+
if (type !== "sprite" && type !== "model")
|
|
331
|
+
continue;
|
|
332
|
+
const id = firstField(embedded, "id");
|
|
333
|
+
const payload = firstField(embedded, "data");
|
|
334
|
+
if (id === undefined || payload === undefined)
|
|
335
|
+
continue;
|
|
336
|
+
let document;
|
|
337
|
+
try {
|
|
338
|
+
document = parseSceneTextFormat(payload);
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (!(error instanceof SceneTextFormatError))
|
|
341
|
+
throw error;
|
|
342
|
+
unresolved.push(`${displayPath}: the embedded ${type} "${id}" could not be read (${error.message})`);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (type === "sprite") {
|
|
346
|
+
const tileSet = firstField(document, "tile_set");
|
|
347
|
+
if (tileSet !== undefined)
|
|
348
|
+
sources.set(id, { kind: "sprite", path: resourceKey(tileSet) });
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const animations = firstField(document, "animations");
|
|
352
|
+
if (animations !== undefined)
|
|
353
|
+
claimModel(id, animations);
|
|
354
|
+
}
|
|
355
|
+
for (const referenced of childrenOf2(object, "components")) {
|
|
356
|
+
const component = firstField(referenced, "component");
|
|
357
|
+
const id = firstField(referenced, "id");
|
|
358
|
+
if (id === undefined || component === undefined)
|
|
359
|
+
continue;
|
|
360
|
+
if (component.endsWith(SPRITE_SUFFIX2)) {
|
|
361
|
+
const tileSet = assets.tileSetBySprite.get(resourceKey(component));
|
|
362
|
+
if (tileSet === undefined) {
|
|
363
|
+
unresolved.push(`${displayPath}: the sprite component "${id}" names ${component}, which is not among the project's sprite documents`);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
sources.set(id, { kind: "sprite", path: tileSet });
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (!component.endsWith(MODEL_SUFFIX2))
|
|
370
|
+
continue;
|
|
371
|
+
const resource = resourceKey(component);
|
|
372
|
+
if (!assets.modelDocuments.has(resource)) {
|
|
373
|
+
unresolved.push(`${displayPath}: the model component "${id}" names ${component}, which is not among the project's model documents`);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const animations = assets.animationSourceByModel.get(resource);
|
|
377
|
+
if (animations !== undefined)
|
|
378
|
+
claimModel(id, animations);
|
|
379
|
+
}
|
|
380
|
+
return sources;
|
|
381
|
+
}
|
|
382
|
+
function scriptResourcesOf(object) {
|
|
383
|
+
const resources = [];
|
|
384
|
+
for (const component of childrenOf2(object, "components")) {
|
|
385
|
+
const resource = firstField(component, "component");
|
|
386
|
+
if (resource === undefined || !resource.endsWith(SCRIPT_SUFFIX))
|
|
387
|
+
continue;
|
|
388
|
+
const key = resourceKey(resource);
|
|
389
|
+
if (key !== SCRIPT_SUFFIX.slice(1))
|
|
390
|
+
resources.push(key);
|
|
391
|
+
}
|
|
392
|
+
return resources;
|
|
393
|
+
}
|
|
394
|
+
function isGameObject(message) {
|
|
395
|
+
return message.messages.has("components") || message.messages.has("embedded_components");
|
|
396
|
+
}
|
|
397
|
+
function buildComponentAnimationIndex(input) {
|
|
398
|
+
const unresolved = [];
|
|
399
|
+
const assets = readAssets(input.assets, unresolved);
|
|
195
400
|
const byScriptResource = new Map;
|
|
401
|
+
const sourceByScriptResource = new Map;
|
|
196
402
|
const claimedBy = new Map;
|
|
403
|
+
function walk(message, blockName, displayPath) {
|
|
404
|
+
if (isGameObject(message)) {
|
|
405
|
+
claim(message, displayPath);
|
|
406
|
+
}
|
|
407
|
+
if (blockName === "embedded_instances") {
|
|
408
|
+
for (const payload of message.fields.get("data") ?? []) {
|
|
409
|
+
try {
|
|
410
|
+
walk(parseSceneTextFormat(payload), "", displayPath);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (!(error instanceof SceneTextFormatError))
|
|
413
|
+
throw error;
|
|
414
|
+
unresolved.push(`${displayPath}: an embedded game object could not be read (${error.message})`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
for (const [name, nested] of message.messages) {
|
|
419
|
+
for (const child of nested) {
|
|
420
|
+
walk(child, name, displayPath);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function idsOf(id, source, displayPath) {
|
|
425
|
+
if (source.kind === "sprite") {
|
|
426
|
+
const declared = assets.animationsByTileSet.get(source.path);
|
|
427
|
+
if (declared !== undefined)
|
|
428
|
+
return new Map([...declared].map((name) => [name, source.path]));
|
|
429
|
+
unresolved.push(`${displayPath}: the sprite component "${id}" names the tile source /${source.path}, which is not among the project's asset documents`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const resolved = resolveAnimationSet({
|
|
433
|
+
path: source.path,
|
|
434
|
+
prefix: "",
|
|
435
|
+
stack: new Set,
|
|
436
|
+
assets,
|
|
437
|
+
unresolved
|
|
438
|
+
});
|
|
439
|
+
if (resolved !== undefined)
|
|
440
|
+
return resolved;
|
|
441
|
+
if (!assets.entriesByAnimationSet.has(source.path)) {
|
|
442
|
+
unresolved.push(`${displayPath}: the model component "${id}" names the animation set /${source.path}, which is not among the project's asset documents`);
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
function claim(object, displayPath) {
|
|
447
|
+
const scripts = scriptResourcesOf(object);
|
|
448
|
+
if (scripts.length === 0)
|
|
449
|
+
return;
|
|
450
|
+
const sources = componentAnimationSources(object, displayPath, assets, unresolved);
|
|
451
|
+
const animations = new Map;
|
|
452
|
+
const declaringSources = new Map;
|
|
453
|
+
for (const [id, source] of sources) {
|
|
454
|
+
const resolved = idsOf(id, source, displayPath);
|
|
455
|
+
if (resolved === undefined)
|
|
456
|
+
continue;
|
|
457
|
+
animations.set(id, new Set(resolved.keys()));
|
|
458
|
+
declaringSources.set(id, resolved);
|
|
459
|
+
}
|
|
460
|
+
for (const key of scripts) {
|
|
461
|
+
const owner = claimedBy.get(key);
|
|
462
|
+
if (owner !== undefined) {
|
|
463
|
+
byScriptResource.delete(key);
|
|
464
|
+
sourceByScriptResource.delete(key);
|
|
465
|
+
unresolved.push(`${key}: claimed by both ${owner} and ${displayPath}, so its component animations are ambiguous`);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
claimedBy.set(key, displayPath);
|
|
469
|
+
byScriptResource.set(key, animations);
|
|
470
|
+
sourceByScriptResource.set(key, declaringSources);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
for (const [path, text] of input.scenes) {
|
|
474
|
+
const displayPath = normalizeDocumentKey(path);
|
|
475
|
+
try {
|
|
476
|
+
walk(parseSceneTextFormat(text), "", displayPath);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (!(error instanceof SceneTextFormatError))
|
|
479
|
+
throw error;
|
|
480
|
+
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return { byScriptResource, sourceByScriptResource, unresolved };
|
|
484
|
+
}
|
|
485
|
+
function componentIdOfSameObjectAddress(address) {
|
|
486
|
+
if (!address.startsWith("#"))
|
|
487
|
+
return;
|
|
488
|
+
const id = address.slice(1);
|
|
489
|
+
if (id === "" || id.includes("/") || id.includes("#"))
|
|
490
|
+
return;
|
|
491
|
+
return id;
|
|
492
|
+
}
|
|
493
|
+
// src/config-key-index.ts
|
|
494
|
+
function sectionNameOf(line) {
|
|
495
|
+
const match = line.trim().match(/^\[(.+)\]$/);
|
|
496
|
+
return match?.[1];
|
|
497
|
+
}
|
|
498
|
+
function buildConfigKeyIndex(gameProjectText) {
|
|
499
|
+
const keys = new Set;
|
|
500
|
+
let section;
|
|
501
|
+
for (const line of gameProjectText.split(`
|
|
502
|
+
`)) {
|
|
503
|
+
const header = sectionNameOf(line);
|
|
504
|
+
if (header !== undefined) {
|
|
505
|
+
section = header;
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (section === undefined)
|
|
509
|
+
continue;
|
|
510
|
+
const separator = line.indexOf("=");
|
|
511
|
+
if (separator === -1)
|
|
512
|
+
continue;
|
|
513
|
+
const key = line.slice(0, separator).trim();
|
|
514
|
+
if (key === "")
|
|
515
|
+
continue;
|
|
516
|
+
keys.add(`${section}.${key}`);
|
|
517
|
+
}
|
|
518
|
+
return keys;
|
|
519
|
+
}
|
|
520
|
+
function readGameProjectSetting(gameProjectText, section, key) {
|
|
521
|
+
let current;
|
|
522
|
+
for (const line of gameProjectText.split(`
|
|
523
|
+
`)) {
|
|
524
|
+
const header = sectionNameOf(line);
|
|
525
|
+
if (header !== undefined) {
|
|
526
|
+
current = header;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (current !== section)
|
|
530
|
+
continue;
|
|
531
|
+
const separator = line.indexOf("=");
|
|
532
|
+
if (separator === -1)
|
|
533
|
+
continue;
|
|
534
|
+
if (line.slice(0, separator).trim() !== key)
|
|
535
|
+
continue;
|
|
536
|
+
const value = line.slice(separator + 1).trim();
|
|
537
|
+
if (value !== "")
|
|
538
|
+
return value;
|
|
539
|
+
}
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
// src/gui-flipbook-index.ts
|
|
543
|
+
var SCRIPT_SUFFIX2 = ".gui_script";
|
|
544
|
+
function scriptKeyOf(script) {
|
|
545
|
+
if (!script.endsWith(SCRIPT_SUFFIX2))
|
|
546
|
+
return;
|
|
547
|
+
const key = resourceKey(script);
|
|
548
|
+
return key === SCRIPT_SUFFIX2 ? undefined : key;
|
|
549
|
+
}
|
|
550
|
+
function buildGuiFlipbookIndex(input) {
|
|
197
551
|
const unresolved = [];
|
|
198
|
-
|
|
552
|
+
const animationsBySource = readAnimationSources(input.assets, unresolved);
|
|
553
|
+
const byScriptResource = new Map;
|
|
554
|
+
const claimedBy = new Map;
|
|
555
|
+
for (const [path, text] of input.scenes) {
|
|
556
|
+
const displayPath = normalizeDocumentKey(path);
|
|
199
557
|
let document;
|
|
200
558
|
try {
|
|
201
559
|
document = parseSceneTextFormat(text);
|
|
@@ -212,6 +570,83 @@ function buildGuiNodeIndex(documents) {
|
|
|
212
570
|
if (key === undefined)
|
|
213
571
|
continue;
|
|
214
572
|
const owner = claimedBy.get(key);
|
|
573
|
+
if (owner !== undefined) {
|
|
574
|
+
byScriptResource.delete(key);
|
|
575
|
+
unresolved.push(`${key}: claimed by both ${owner} and ${displayPath}, so its flipbook animations are ambiguous`);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
claimedBy.set(key, displayPath);
|
|
579
|
+
const declaredBy = new Map;
|
|
580
|
+
for (const message of document.messages.get("textures") ?? []) {
|
|
581
|
+
for (const texture of message.fields.get("texture") ?? []) {
|
|
582
|
+
if (texture === "")
|
|
583
|
+
continue;
|
|
584
|
+
const source = resourceKey(texture);
|
|
585
|
+
const ids = animationsBySource.get(source);
|
|
586
|
+
if (ids === undefined) {
|
|
587
|
+
unresolved.push(`${displayPath}: the texture ${texture} is not among the project's asset documents`);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
for (const id of ids) {
|
|
591
|
+
const declarers = declaredBy.get(id);
|
|
592
|
+
if (declarers) {
|
|
593
|
+
declarers.add(source);
|
|
594
|
+
} else {
|
|
595
|
+
declaredBy.set(id, new Set([source]));
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
byScriptResource.set(key, declaredBy);
|
|
601
|
+
}
|
|
602
|
+
return { byScriptResource, unresolved };
|
|
603
|
+
}
|
|
604
|
+
// src/gui-node-index.ts
|
|
605
|
+
var SCRIPT_SUFFIX3 = ".gui_script";
|
|
606
|
+
function scriptKeyOf2(script) {
|
|
607
|
+
if (!script.endsWith(SCRIPT_SUFFIX3))
|
|
608
|
+
return;
|
|
609
|
+
const key = script.startsWith("/") ? script.slice(1) : script;
|
|
610
|
+
return key === SCRIPT_SUFFIX3 ? undefined : key;
|
|
611
|
+
}
|
|
612
|
+
function guiScriptResourceOf(document) {
|
|
613
|
+
const [script] = document.fields.get("script") ?? [];
|
|
614
|
+
return script === undefined ? undefined : scriptKeyOf2(script);
|
|
615
|
+
}
|
|
616
|
+
function guiScriptResourcesOf(documents) {
|
|
617
|
+
const scripts = new Map;
|
|
618
|
+
for (const [displayPath, text] of documents) {
|
|
619
|
+
let resource;
|
|
620
|
+
try {
|
|
621
|
+
resource = guiScriptResourceOf(parseSceneTextFormat(text));
|
|
622
|
+
} catch (error) {
|
|
623
|
+
if (!(error instanceof SceneTextFormatError))
|
|
624
|
+
throw error;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (resource !== undefined)
|
|
628
|
+
scripts.set(displayPath, resource);
|
|
629
|
+
}
|
|
630
|
+
return scripts;
|
|
631
|
+
}
|
|
632
|
+
function buildGuiNodeIndex(documents) {
|
|
633
|
+
const byScriptResource = new Map;
|
|
634
|
+
const claimedBy = new Map;
|
|
635
|
+
const unresolved = [];
|
|
636
|
+
for (const [displayPath, text] of documents) {
|
|
637
|
+
let document;
|
|
638
|
+
try {
|
|
639
|
+
document = parseSceneTextFormat(text);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
if (!(error instanceof SceneTextFormatError))
|
|
642
|
+
throw error;
|
|
643
|
+
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const key = guiScriptResourceOf(document);
|
|
647
|
+
if (key === undefined)
|
|
648
|
+
continue;
|
|
649
|
+
const owner = claimedBy.get(key);
|
|
215
650
|
if (owner !== undefined) {
|
|
216
651
|
byScriptResource.delete(key);
|
|
217
652
|
unresolved.push(`${key}: claimed by both ${owner} and ${displayPath}, so its node ids are ambiguous`);
|
|
@@ -252,6 +687,304 @@ function buildInputActionIndex(documents) {
|
|
|
252
687
|
}
|
|
253
688
|
return actions;
|
|
254
689
|
}
|
|
690
|
+
// src/project-resources.ts
|
|
691
|
+
var SCAFFOLDED_DEFIGNORE_LINES = ["/node_modules", "/.defold-types", "/.vscode"];
|
|
692
|
+
function isDefignoredPath(displayPath) {
|
|
693
|
+
const rooted = `/${displayPath.replace(/^\/+/, "")}`;
|
|
694
|
+
return SCAFFOLDED_DEFIGNORE_LINES.some((line) => rooted === line || rooted.startsWith(`${line}/`));
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/scene-documents.ts
|
|
698
|
+
var SCENE_EXTENSIONS = [".go", ".collection"];
|
|
699
|
+
var GUI_EXTENSIONS = [".gui"];
|
|
700
|
+
var INPUT_BINDING_EXTENSIONS = [".input_binding"];
|
|
701
|
+
var COLLECTION_REFERENCE_EXTENSIONS = [".collectionproxy", ".collectionfactory"];
|
|
702
|
+
var ANIMATION_ASSET_EXTENSIONS = [
|
|
703
|
+
".atlas",
|
|
704
|
+
".tilesource",
|
|
705
|
+
".sprite",
|
|
706
|
+
".model",
|
|
707
|
+
".animationset"
|
|
708
|
+
];
|
|
709
|
+
var LIBRARY_SCENE_EXTENSIONS = [
|
|
710
|
+
...SCENE_EXTENSIONS,
|
|
711
|
+
...GUI_EXTENSIONS,
|
|
712
|
+
...ANIMATION_ASSET_EXTENSIONS,
|
|
713
|
+
...COLLECTION_REFERENCE_EXTENSIONS
|
|
714
|
+
];
|
|
715
|
+
var PROJECT_EXTENSIONS = [".project"];
|
|
716
|
+
var GAME_PROJECT_DOCUMENT = "game.project";
|
|
717
|
+
function displayPathOf(projectRoot, filePath) {
|
|
718
|
+
const path = filePath.replace(/\\/g, "/");
|
|
719
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
720
|
+
return path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;
|
|
721
|
+
}
|
|
722
|
+
function isBuildOutput(displayPath) {
|
|
723
|
+
return displayPath.split("/").includes("build");
|
|
724
|
+
}
|
|
725
|
+
function isExcludedProjectPath(displayPath) {
|
|
726
|
+
return isBuildOutput(displayPath) || isDefignoredPath(displayPath);
|
|
727
|
+
}
|
|
728
|
+
function readSceneDocuments(host, projectRoot, extensions = SCENE_EXTENSIONS) {
|
|
729
|
+
const documents = new Map;
|
|
730
|
+
const origins = new Map;
|
|
731
|
+
const unreadable = [];
|
|
732
|
+
const paths = [];
|
|
733
|
+
if (!host.readDirectory) {
|
|
734
|
+
unreadable.push("the editor host cannot enumerate project files, so no scene source was read");
|
|
735
|
+
return { documents, origins, unreadable, paths };
|
|
736
|
+
}
|
|
737
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
738
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
739
|
+
if (isExcludedProjectPath(displayPath))
|
|
740
|
+
continue;
|
|
741
|
+
const text = host.readFile(filePath);
|
|
742
|
+
if (text === undefined) {
|
|
743
|
+
unreadable.push(`${displayPath}: could not be read`);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
documents.set(displayPath, text);
|
|
747
|
+
paths.push(filePath);
|
|
748
|
+
}
|
|
749
|
+
const library = readLibrarySceneDocuments(host, projectRoot, extensions);
|
|
750
|
+
unreadable.push(...library.unreadable);
|
|
751
|
+
for (const { displayPath, hostPath, url, text } of library.documents) {
|
|
752
|
+
if (documents.has(displayPath)) {
|
|
753
|
+
unreadable.push(`${displayPath}: also declared by ${url}; the project's own file is used`);
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
documents.set(displayPath, text);
|
|
757
|
+
origins.set(displayPath, url);
|
|
758
|
+
paths.push(hostPath);
|
|
759
|
+
}
|
|
760
|
+
return { documents, origins, unreadable, paths };
|
|
761
|
+
}
|
|
762
|
+
function listProjectResourcePaths(host, projectRoot, extensions) {
|
|
763
|
+
if (!host.readDirectory)
|
|
764
|
+
return new Set;
|
|
765
|
+
const paths = [];
|
|
766
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
767
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
768
|
+
if (isExcludedProjectPath(displayPath))
|
|
769
|
+
continue;
|
|
770
|
+
if (!extensions.some((extension) => displayPath.endsWith(extension)))
|
|
771
|
+
continue;
|
|
772
|
+
paths.push(`/${displayPath}`);
|
|
773
|
+
}
|
|
774
|
+
return new Set(paths.sort());
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/library-dependencies.ts
|
|
778
|
+
function isProjectHeader(line) {
|
|
779
|
+
return line.trim() === "[project]";
|
|
780
|
+
}
|
|
781
|
+
function isSectionHeader(line) {
|
|
782
|
+
return /^\[.+\]\s*$/.test(line.trim());
|
|
783
|
+
}
|
|
784
|
+
function readGameProjectDependencies(gameProjectText) {
|
|
785
|
+
const lines = gameProjectText.split(`
|
|
786
|
+
`);
|
|
787
|
+
const deps = [];
|
|
788
|
+
let inProject = false;
|
|
789
|
+
for (const line of lines) {
|
|
790
|
+
if (isSectionHeader(line)) {
|
|
791
|
+
inProject = isProjectHeader(line);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (!inProject) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
const match = line.match(/^dependencies#(\d+)\s*=\s*(.+)$/);
|
|
798
|
+
if (match?.[1] !== undefined && match[2] !== undefined) {
|
|
799
|
+
deps.push({ index: Number(match[1]), url: match[2] });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return deps;
|
|
803
|
+
}
|
|
804
|
+
function archiveWrapperOf(entries) {
|
|
805
|
+
let wrapper;
|
|
806
|
+
for (const entry of entries) {
|
|
807
|
+
const separator = entry.indexOf("/");
|
|
808
|
+
if (separator <= 0) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const segment = entry.slice(0, separator);
|
|
812
|
+
if (wrapper === undefined) {
|
|
813
|
+
wrapper = segment;
|
|
814
|
+
} else if (wrapper !== segment) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return wrapper;
|
|
819
|
+
}
|
|
820
|
+
function readLibraryIncludeDirs(gameProjectText) {
|
|
821
|
+
let inLibrary = false;
|
|
822
|
+
for (const line of gameProjectText.split(`
|
|
823
|
+
`)) {
|
|
824
|
+
if (isSectionHeader(line)) {
|
|
825
|
+
inLibrary = line.trim() === "[library]";
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
if (!inLibrary) {
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
const match = line.match(/^include_dirs\s*=\s*(.*)$/);
|
|
832
|
+
if (match?.[1] !== undefined) {
|
|
833
|
+
return match[1].split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return [];
|
|
837
|
+
}
|
|
838
|
+
function isContainedResourcePath(path) {
|
|
839
|
+
if (path.length === 0 || path.includes("\\")) {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
return path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
843
|
+
}
|
|
844
|
+
function libraryIncludedEntries(entries, gameProjectText) {
|
|
845
|
+
if (gameProjectText === undefined) {
|
|
846
|
+
return {
|
|
847
|
+
shared: [],
|
|
848
|
+
reasons: ["ships no game.project, so it declares no [library] include_dirs"],
|
|
849
|
+
refused: []
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
const includeDirs = new Set(readLibraryIncludeDirs(gameProjectText));
|
|
853
|
+
if (includeDirs.size === 0) {
|
|
854
|
+
return {
|
|
855
|
+
shared: [],
|
|
856
|
+
reasons: ["its game.project declares no [library] include_dirs"],
|
|
857
|
+
refused: []
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
const wrapper = archiveWrapperOf(entries);
|
|
861
|
+
const shared = [];
|
|
862
|
+
const refused = [];
|
|
863
|
+
for (const entry of entries) {
|
|
864
|
+
const path = wrapper === undefined ? entry : entry.slice(wrapper.length + 1);
|
|
865
|
+
const [head] = path.split("/");
|
|
866
|
+
if (head === undefined || !includeDirs.has(head)) {
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const dot = path.lastIndexOf(".");
|
|
870
|
+
if (dot === -1 || !LIBRARY_SCENE_EXTENSIONS.includes(path.slice(dot).toLowerCase())) {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (!isContainedResourcePath(path)) {
|
|
874
|
+
refused.push(entry);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
shared.push({ entry, path });
|
|
878
|
+
}
|
|
879
|
+
shared.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
880
|
+
return { shared, reasons: [], refused };
|
|
881
|
+
}
|
|
882
|
+
var LIBRARY_DEPENDENCY_ROOT = ".defold-types/dependencies";
|
|
883
|
+
var LIBRARY_DEPENDENCY_MANIFEST = `${LIBRARY_DEPENDENCY_ROOT}/dependencies.json`;
|
|
884
|
+
function joinProject(projectRoot, relative) {
|
|
885
|
+
return `${projectRoot.replace(/\\/g, "/").replace(/\/+$/, "")}/${relative}`;
|
|
886
|
+
}
|
|
887
|
+
function readMaterializedKeys(manifestText) {
|
|
888
|
+
let parsed;
|
|
889
|
+
try {
|
|
890
|
+
parsed = JSON.parse(manifestText);
|
|
891
|
+
} catch {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const entries = parsed?.dependencies;
|
|
895
|
+
if (!Array.isArray(entries))
|
|
896
|
+
return;
|
|
897
|
+
const keyByUrl = new Map;
|
|
898
|
+
for (const entry of entries) {
|
|
899
|
+
const { key, url } = entry ?? {};
|
|
900
|
+
if (typeof key === "string" && typeof url === "string")
|
|
901
|
+
keyByUrl.set(url, key);
|
|
902
|
+
}
|
|
903
|
+
return keyByUrl;
|
|
904
|
+
}
|
|
905
|
+
function readLibrarySceneDocuments(host, projectRoot, extensions = LIBRARY_SCENE_EXTENSIONS) {
|
|
906
|
+
const documents = [];
|
|
907
|
+
const unreadable = [];
|
|
908
|
+
if (!host.readDirectory)
|
|
909
|
+
return { documents, unreadable };
|
|
910
|
+
const gameProjectText = host.readFile(joinProject(projectRoot, GAME_PROJECT_DOCUMENT));
|
|
911
|
+
if (gameProjectText === undefined)
|
|
912
|
+
return { documents, unreadable };
|
|
913
|
+
const declared = [...readGameProjectDependencies(gameProjectText)].sort((a, b) => a.index - b.index);
|
|
914
|
+
if (declared.length === 0)
|
|
915
|
+
return { documents, unreadable };
|
|
916
|
+
const manifestText = host.readFile(joinProject(projectRoot, LIBRARY_DEPENDENCY_MANIFEST));
|
|
917
|
+
if (manifestText === undefined) {
|
|
918
|
+
for (const { url } of declared) {
|
|
919
|
+
unreadable.push(`${url}: is declared by game.project but ${LIBRARY_DEPENDENCY_MANIFEST} is absent`);
|
|
920
|
+
}
|
|
921
|
+
return { documents, unreadable };
|
|
922
|
+
}
|
|
923
|
+
const keyByUrl = readMaterializedKeys(manifestText);
|
|
924
|
+
if (keyByUrl === undefined) {
|
|
925
|
+
for (const { url } of declared) {
|
|
926
|
+
unreadable.push(`${url}: is declared by game.project but ${LIBRARY_DEPENDENCY_MANIFEST} is not a readable dependency manifest`);
|
|
927
|
+
}
|
|
928
|
+
return { documents, unreadable };
|
|
929
|
+
}
|
|
930
|
+
const resolved = [];
|
|
931
|
+
const urlByKey = new Map;
|
|
932
|
+
for (const { url } of declared) {
|
|
933
|
+
const key = keyByUrl.get(url);
|
|
934
|
+
if (key === undefined) {
|
|
935
|
+
unreadable.push(`${url}: is declared by game.project but was not materialized by the last resolve`);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
resolved.push({ key, url });
|
|
939
|
+
urlByKey.set(key, url);
|
|
940
|
+
}
|
|
941
|
+
const root = joinProject(projectRoot, LIBRARY_DEPENDENCY_ROOT);
|
|
942
|
+
const filesByKey = new Map;
|
|
943
|
+
const stray = new Set;
|
|
944
|
+
for (const filePath of host.readDirectory(root, extensions)) {
|
|
945
|
+
const relative = displayPathOf(root, filePath);
|
|
946
|
+
if (relative === filePath.replace(/\\/g, "/"))
|
|
947
|
+
continue;
|
|
948
|
+
const separator = relative.indexOf("/");
|
|
949
|
+
if (separator <= 0)
|
|
950
|
+
continue;
|
|
951
|
+
const key = relative.slice(0, separator);
|
|
952
|
+
const url = urlByKey.get(key);
|
|
953
|
+
if (url === undefined) {
|
|
954
|
+
stray.add(key);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
const bucket = filesByKey.get(key);
|
|
958
|
+
if (bucket === undefined)
|
|
959
|
+
filesByKey.set(key, [filePath]);
|
|
960
|
+
else
|
|
961
|
+
bucket.push(filePath);
|
|
962
|
+
}
|
|
963
|
+
const claimed = new Map;
|
|
964
|
+
for (const { key, url } of resolved) {
|
|
965
|
+
for (const hostPath of (filesByKey.get(key) ?? []).sort()) {
|
|
966
|
+
const displayPath = displayPathOf(root, hostPath).slice(key.length + 1);
|
|
967
|
+
if (displayPath === "")
|
|
968
|
+
continue;
|
|
969
|
+
const owner = claimed.get(displayPath);
|
|
970
|
+
if (owner !== undefined) {
|
|
971
|
+
unreadable.push(`${displayPath}: also declared by ${url}; ${owner}'s file is used`);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const text = host.readFile(hostPath);
|
|
975
|
+
if (text === undefined) {
|
|
976
|
+
unreadable.push(`${displayPath}: could not be read from ${url}`);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
claimed.set(displayPath, url);
|
|
980
|
+
documents.push({ displayPath, hostPath, url, text });
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
for (const key of [...stray].sort()) {
|
|
984
|
+
unreadable.push(`${LIBRARY_DEPENDENCY_ROOT}/${key}: is materialized but no longer declared by game.project, so its scenes are left out`);
|
|
985
|
+
}
|
|
986
|
+
return { documents, unreadable };
|
|
987
|
+
}
|
|
255
988
|
// src/main-entry-factory-imports.ts
|
|
256
989
|
import * as ts3 from "typescript";
|
|
257
990
|
|
|
@@ -826,25 +1559,30 @@ function getProgramDiagnostics(program, sourceFile) {
|
|
|
826
1559
|
});
|
|
827
1560
|
return diagnostics.filter((d) => d.file !== undefined && !isAmbient(d.file.fileName));
|
|
828
1561
|
}
|
|
829
|
-
// src/project-resources.ts
|
|
830
|
-
var SCAFFOLDED_DEFIGNORE_LINES = ["/node_modules", "/.defold-types", "/.vscode"];
|
|
831
|
-
function isDefignoredPath(displayPath) {
|
|
832
|
-
const rooted = `/${displayPath.replace(/^\/+/, "")}`;
|
|
833
|
-
return SCAFFOLDED_DEFIGNORE_LINES.some((line) => rooted === line || rooted.startsWith(`${line}/`));
|
|
834
|
-
}
|
|
835
1562
|
// src/scene-component-index.ts
|
|
836
1563
|
var COMPONENT_BLOCKS = new Set(["components", "embedded_components"]);
|
|
837
1564
|
var EMBEDDING_BLOCKS = new Set(["embedded_instances", "embedded_components"]);
|
|
838
|
-
function
|
|
1565
|
+
function resourceKey2(resource) {
|
|
1566
|
+
return resource.startsWith("/") ? resource.slice(1) : resource;
|
|
1567
|
+
}
|
|
1568
|
+
function collect(message, blockName, depth, displayPath, declarations, incomplete) {
|
|
839
1569
|
if (COMPONENT_BLOCKS.has(blockName)) {
|
|
840
|
-
|
|
841
|
-
|
|
1570
|
+
const declared = message.fields.get("id") ?? [];
|
|
1571
|
+
for (const id of declared) {
|
|
1572
|
+
declarations.ids.add(id);
|
|
1573
|
+
}
|
|
1574
|
+
if (blockName === "components") {
|
|
1575
|
+
const [id] = declared;
|
|
1576
|
+
const [component] = message.fields.get("component") ?? [];
|
|
1577
|
+
if (id !== undefined && id !== "" && component !== undefined) {
|
|
1578
|
+
declarations.resources.set(id, resourceKey2(component));
|
|
1579
|
+
}
|
|
842
1580
|
}
|
|
843
1581
|
}
|
|
844
1582
|
if (EMBEDDING_BLOCKS.has(blockName)) {
|
|
845
1583
|
for (const payload of message.fields.get("data") ?? []) {
|
|
846
1584
|
try {
|
|
847
|
-
collect(parseSceneTextFormat(payload), "", depth + 1, displayPath,
|
|
1585
|
+
collect(parseSceneTextFormat(payload), "", depth + 1, displayPath, declarations, incomplete);
|
|
848
1586
|
} catch (error) {
|
|
849
1587
|
if (!(error instanceof SceneTextFormatError))
|
|
850
1588
|
throw error;
|
|
@@ -854,10 +1592,18 @@ function collect(message, blockName, depth, displayPath, ids, incomplete) {
|
|
|
854
1592
|
}
|
|
855
1593
|
for (const [name, nested] of message.messages) {
|
|
856
1594
|
for (const child of nested) {
|
|
857
|
-
collect(child, name, depth, displayPath,
|
|
1595
|
+
collect(child, name, depth, displayPath, declarations, incomplete);
|
|
858
1596
|
}
|
|
859
1597
|
}
|
|
860
1598
|
}
|
|
1599
|
+
function collectComponentDeclarations(message, displayPath, incomplete, blockName = "") {
|
|
1600
|
+
const declarations = { ids: new Set, resources: new Map };
|
|
1601
|
+
collect(message, blockName, 0, displayPath, declarations, incomplete);
|
|
1602
|
+
return declarations;
|
|
1603
|
+
}
|
|
1604
|
+
function collectComponentIds(message, displayPath, incomplete, blockName = "") {
|
|
1605
|
+
return collectComponentDeclarations(message, displayPath, incomplete, blockName).ids;
|
|
1606
|
+
}
|
|
861
1607
|
function buildSceneComponentIndex(documents) {
|
|
862
1608
|
const ids = new Set;
|
|
863
1609
|
const incomplete = [];
|
|
@@ -867,7 +1613,9 @@ function buildSceneComponentIndex(documents) {
|
|
|
867
1613
|
}
|
|
868
1614
|
for (const [displayPath, text] of documents) {
|
|
869
1615
|
try {
|
|
870
|
-
|
|
1616
|
+
for (const id of collectComponentIds(parseSceneTextFormat(text), displayPath, incomplete)) {
|
|
1617
|
+
ids.add(id);
|
|
1618
|
+
}
|
|
871
1619
|
} catch (error) {
|
|
872
1620
|
if (!(error instanceof SceneTextFormatError))
|
|
873
1621
|
throw error;
|
|
@@ -876,23 +1624,238 @@ function buildSceneComponentIndex(documents) {
|
|
|
876
1624
|
}
|
|
877
1625
|
return { ids, incomplete };
|
|
878
1626
|
}
|
|
1627
|
+
|
|
879
1628
|
// src/scene-object-path-index.ts
|
|
880
1629
|
var LEAF_BLOCKS = ["instances", "embedded_instances"];
|
|
881
|
-
function
|
|
1630
|
+
function resourceKey3(resource) {
|
|
1631
|
+
return resource.startsWith("/") ? resource.slice(1) : resource;
|
|
1632
|
+
}
|
|
1633
|
+
function firstField2(message, name) {
|
|
1634
|
+
return (message.fields.get(name) ?? [])[0];
|
|
1635
|
+
}
|
|
1636
|
+
function childrenOf3(message, name) {
|
|
1637
|
+
return message.messages.get(name) ?? [];
|
|
1638
|
+
}
|
|
1639
|
+
function buildSceneObjectPathIndex(documents, roles) {
|
|
1640
|
+
const incomplete = [];
|
|
1641
|
+
if (documents.size === 0) {
|
|
1642
|
+
incomplete.push("no scene sources were read, so no game-object path can be proven absent");
|
|
1643
|
+
incomplete.push(...roles.incomplete);
|
|
1644
|
+
return {
|
|
1645
|
+
paths: new Set,
|
|
1646
|
+
declaredIn: new Map,
|
|
1647
|
+
componentsOf: new Map,
|
|
1648
|
+
componentResourcesOf: new Map,
|
|
1649
|
+
incomplete
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
const parsed = new Map;
|
|
1653
|
+
for (const [displayPath, text] of documents) {
|
|
1654
|
+
try {
|
|
1655
|
+
parsed.set(displayPath, parseSceneTextFormat(text));
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
if (!(error instanceof SceneTextFormatError))
|
|
1658
|
+
throw error;
|
|
1659
|
+
incomplete.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
const composed = new Map;
|
|
1663
|
+
const inProgress = new Set;
|
|
1664
|
+
const prototypeDeclarations = new Map;
|
|
1665
|
+
function declarationsOfPrototype(key) {
|
|
1666
|
+
const done = prototypeDeclarations.get(key);
|
|
1667
|
+
if (done !== undefined || prototypeDeclarations.has(key))
|
|
1668
|
+
return done;
|
|
1669
|
+
const document = parsed.get(key);
|
|
1670
|
+
let declarations;
|
|
1671
|
+
if (document !== undefined) {
|
|
1672
|
+
const collected = collectComponentDeclarations(document, key, incomplete);
|
|
1673
|
+
declarations = { components: [...collected.ids].sort(), resources: collected.resources };
|
|
1674
|
+
}
|
|
1675
|
+
prototypeDeclarations.set(key, declarations);
|
|
1676
|
+
return declarations;
|
|
1677
|
+
}
|
|
1678
|
+
function pathsOf(displayPath) {
|
|
1679
|
+
const done = composed.get(displayPath);
|
|
1680
|
+
if (done !== undefined)
|
|
1681
|
+
return done;
|
|
1682
|
+
if (inProgress.has(displayPath)) {
|
|
1683
|
+
incomplete.push(`${displayPath}: is instanced inside itself through a cycle of collection references, so its paths cannot be composed`);
|
|
1684
|
+
return new Map;
|
|
1685
|
+
}
|
|
1686
|
+
const document = parsed.get(displayPath);
|
|
1687
|
+
if (document === undefined)
|
|
1688
|
+
return new Map;
|
|
1689
|
+
inProgress.add(displayPath);
|
|
1690
|
+
const paths2 = new Map;
|
|
1691
|
+
for (const blockName of LEAF_BLOCKS) {
|
|
1692
|
+
for (const block of childrenOf3(document, blockName)) {
|
|
1693
|
+
const id = firstField2(block, "id");
|
|
1694
|
+
if (id === undefined || id === "")
|
|
1695
|
+
continue;
|
|
1696
|
+
let components;
|
|
1697
|
+
let resources;
|
|
1698
|
+
if (blockName === "embedded_instances") {
|
|
1699
|
+
const before = incomplete.length;
|
|
1700
|
+
const declarations = collectComponentDeclarations(block, displayPath, incomplete, blockName);
|
|
1701
|
+
if (incomplete.length === before) {
|
|
1702
|
+
components = [...declarations.ids].sort();
|
|
1703
|
+
resources = declarations.resources;
|
|
1704
|
+
}
|
|
1705
|
+
} else {
|
|
1706
|
+
const prototype = firstField2(block, "prototype");
|
|
1707
|
+
if (prototype === undefined) {
|
|
1708
|
+
incomplete.push(`${displayPath}: the instance "${id}" declares no prototype, so the components it owns cannot be read`);
|
|
1709
|
+
} else if (parsed.has(resourceKey3(prototype))) {
|
|
1710
|
+
const declarations = declarationsOfPrototype(resourceKey3(prototype));
|
|
1711
|
+
components = declarations?.components;
|
|
1712
|
+
resources = declarations?.resources;
|
|
1713
|
+
} else {
|
|
1714
|
+
incomplete.push(`${displayPath}: the instance "${id}" names ${prototype}, which is not among the project's readable scene documents`);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
paths2.set(`/${id}`, { declarer: displayPath, components, resources });
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
for (const block of childrenOf3(document, "collection_instances")) {
|
|
1721
|
+
const id = firstField2(block, "id");
|
|
1722
|
+
const collection = firstField2(block, "collection");
|
|
1723
|
+
if (id === undefined || id === "" || collection === undefined)
|
|
1724
|
+
continue;
|
|
1725
|
+
const key = resourceKey3(collection);
|
|
1726
|
+
if (!parsed.has(key)) {
|
|
1727
|
+
incomplete.push(`${displayPath}: the collection instance "${id}" names ${collection}, which is not among the project's readable scene documents`);
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
for (const [nested, leaf] of pathsOf(key)) {
|
|
1731
|
+
paths2.set(`/${id}${nested}`, leaf);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
inProgress.delete(displayPath);
|
|
1735
|
+
composed.set(displayPath, paths2);
|
|
1736
|
+
return paths2;
|
|
1737
|
+
}
|
|
1738
|
+
for (const displayPath of parsed.keys())
|
|
1739
|
+
pathsOf(displayPath);
|
|
1740
|
+
const worlds = [];
|
|
1741
|
+
if (roles.bootstrap !== undefined)
|
|
1742
|
+
worlds.push(["", roles.bootstrap]);
|
|
1743
|
+
for (const [displayPath, socket] of roles.sockets)
|
|
1744
|
+
worlds.push([`${socket}:`, displayPath]);
|
|
1745
|
+
const paths = new Set;
|
|
1746
|
+
const declaredIn = new Map;
|
|
1747
|
+
const componentsOf = new Map;
|
|
1748
|
+
const componentResourcesOf = new Map;
|
|
1749
|
+
const withheld = new Set;
|
|
1750
|
+
for (const [prefix, displayPath] of worlds) {
|
|
1751
|
+
for (const [path2, leaf] of pathsOf(displayPath)) {
|
|
1752
|
+
const key = `${prefix}${path2}`;
|
|
1753
|
+
paths.add(key);
|
|
1754
|
+
const declarers = declaredIn.get(key);
|
|
1755
|
+
if (declarers === undefined) {
|
|
1756
|
+
declaredIn.set(key, [leaf.declarer]);
|
|
1757
|
+
} else if (!declarers.includes(leaf.declarer)) {
|
|
1758
|
+
declarers.push(leaf.declarer);
|
|
1759
|
+
}
|
|
1760
|
+
if (leaf.components === undefined || leaf.resources === undefined) {
|
|
1761
|
+
withheld.add(key);
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
const owned = componentsOf.get(key);
|
|
1765
|
+
if (owned === undefined) {
|
|
1766
|
+
componentsOf.set(key, [...leaf.components]);
|
|
1767
|
+
} else {
|
|
1768
|
+
for (const id of leaf.components)
|
|
1769
|
+
if (!owned.includes(id))
|
|
1770
|
+
owned.push(id);
|
|
1771
|
+
}
|
|
1772
|
+
const referenced = componentResourcesOf.get(key);
|
|
1773
|
+
if (referenced === undefined) {
|
|
1774
|
+
componentResourcesOf.set(key, new Map(leaf.resources));
|
|
1775
|
+
} else {
|
|
1776
|
+
for (const [id, resource] of leaf.resources) {
|
|
1777
|
+
if (!referenced.has(id))
|
|
1778
|
+
referenced.set(id, resource);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
for (const declarers of declaredIn.values())
|
|
1784
|
+
declarers.sort();
|
|
1785
|
+
for (const key of withheld) {
|
|
1786
|
+
componentsOf.delete(key);
|
|
1787
|
+
componentResourcesOf.delete(key);
|
|
1788
|
+
}
|
|
1789
|
+
for (const owned of componentsOf.values())
|
|
1790
|
+
owned.sort();
|
|
1791
|
+
incomplete.push(...roles.incomplete);
|
|
1792
|
+
return {
|
|
1793
|
+
paths,
|
|
1794
|
+
declaredIn,
|
|
1795
|
+
componentsOf,
|
|
1796
|
+
componentResourcesOf,
|
|
1797
|
+
incomplete: [...new Set(incomplete)]
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// src/scene-address-declaration.ts
|
|
1802
|
+
var BANNER = `// Generated from this project's scenes by \`defold-typescript scene-types\`.
|
|
1803
|
+
// Do not edit: every run rewrites it from the .go/.collection sources.
|
|
1804
|
+
//
|
|
1805
|
+
// Each key is an address the project declares. The aliases in
|
|
1806
|
+
// \`@defold-typescript/types\` stay widened with \`(string & {})\`, so these keys
|
|
1807
|
+
// only add completions — an address composed at runtime is never rejected.
|
|
1808
|
+
`;
|
|
1809
|
+
function componentAddressesOf(documents, paths) {
|
|
1810
|
+
const keys = [...buildSceneComponentIndex(documents).ids].map((id) => `#${id}`);
|
|
1811
|
+
for (const [path2, ids] of paths.componentsOf) {
|
|
1812
|
+
for (const id of ids)
|
|
1813
|
+
keys.push(`${path2}#${id}`);
|
|
1814
|
+
}
|
|
1815
|
+
return keys;
|
|
1816
|
+
}
|
|
1817
|
+
function bodyOf(keys) {
|
|
1818
|
+
if (keys.length === 0)
|
|
1819
|
+
return "{}";
|
|
1820
|
+
const members = keys.map((key) => ` ${JSON.stringify(key)}: true;
|
|
1821
|
+
`).sort().join("");
|
|
1822
|
+
return `{
|
|
1823
|
+
${members} }`;
|
|
1824
|
+
}
|
|
1825
|
+
function buildSceneAddressDeclaration(documents, roles) {
|
|
1826
|
+
const index = buildSceneObjectPathIndex(documents, roles);
|
|
1827
|
+
const components = componentAddressesOf(documents, index);
|
|
1828
|
+
return `${BANNER}
|
|
1829
|
+
declare global {
|
|
1830
|
+
interface SceneGameObjectAddresses ${bodyOf([...index.paths])}
|
|
1831
|
+
|
|
1832
|
+
interface SceneComponentAddresses ${bodyOf(components)}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
export {};
|
|
1836
|
+
`;
|
|
1837
|
+
}
|
|
1838
|
+
// src/scene-collection-roles.ts
|
|
1839
|
+
var EMBEDDING_BLOCKS2 = new Set(["embedded_instances", "embedded_components"]);
|
|
1840
|
+
var REFERENCE_KINDS = [
|
|
1841
|
+
{ extension: ".collectionproxy", type: "collectionproxy", field: "collection" },
|
|
1842
|
+
{ extension: ".collectionfactory", type: "collectionfactory", field: "prototype" }
|
|
1843
|
+
];
|
|
1844
|
+
function resourceKey4(resource) {
|
|
882
1845
|
return resource.startsWith("/") ? resource.slice(1) : resource;
|
|
883
1846
|
}
|
|
884
|
-
function
|
|
1847
|
+
function collectionKey(resource) {
|
|
1848
|
+
return resourceKey4(resource).replace(/\.collectionc$/, ".collection");
|
|
1849
|
+
}
|
|
1850
|
+
function firstField3(message, name) {
|
|
885
1851
|
return (message.fields.get(name) ?? [])[0];
|
|
886
1852
|
}
|
|
887
|
-
function
|
|
888
|
-
return
|
|
1853
|
+
function isCollection(displayPath) {
|
|
1854
|
+
return displayPath.endsWith(".collection");
|
|
889
1855
|
}
|
|
890
|
-
function
|
|
1856
|
+
function buildSceneCollectionRoles(input) {
|
|
1857
|
+
const { documents, references, gameProject } = input;
|
|
891
1858
|
const incomplete = [];
|
|
892
|
-
if (documents.size === 0) {
|
|
893
|
-
incomplete.push("no scene sources were read, so no game-object path can be proven absent");
|
|
894
|
-
return { paths: new Set, declaredIn: new Map, incomplete };
|
|
895
|
-
}
|
|
896
1859
|
const parsed = new Map;
|
|
897
1860
|
for (const [displayPath, text] of documents) {
|
|
898
1861
|
try {
|
|
@@ -905,71 +1868,185 @@ function buildSceneObjectPathIndex(documents) {
|
|
|
905
1868
|
}
|
|
906
1869
|
const instanced = new Set;
|
|
907
1870
|
for (const document of parsed.values()) {
|
|
908
|
-
for (const block of
|
|
909
|
-
const collection =
|
|
1871
|
+
for (const block of document.messages.get("collection_instances") ?? []) {
|
|
1872
|
+
const collection = firstField3(block, "collection");
|
|
910
1873
|
if (collection !== undefined)
|
|
911
|
-
instanced.add(
|
|
1874
|
+
instanced.add(resourceKey4(collection));
|
|
912
1875
|
}
|
|
913
1876
|
}
|
|
914
|
-
const
|
|
915
|
-
const
|
|
916
|
-
function
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return
|
|
1877
|
+
const proxied = new Set;
|
|
1878
|
+
const factoryPrototypes = new Set;
|
|
1879
|
+
function record(kind, resource) {
|
|
1880
|
+
(kind.type === "collectionproxy" ? proxied : factoryPrototypes).add(collectionKey(resource));
|
|
1881
|
+
}
|
|
1882
|
+
function followReferenceDocument(displayPath, resource) {
|
|
1883
|
+
const kind = REFERENCE_KINDS.find((candidate) => resource.endsWith(candidate.extension));
|
|
1884
|
+
if (kind === undefined)
|
|
1885
|
+
return;
|
|
1886
|
+
const text = references.get(resourceKey4(resource));
|
|
1887
|
+
if (text === undefined) {
|
|
1888
|
+
incomplete.push(`${displayPath}: the component ${resource} is not among the project's readable ${kind.extension} documents, so the collection it names is unclassified`);
|
|
1889
|
+
return;
|
|
923
1890
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1891
|
+
let document;
|
|
1892
|
+
try {
|
|
1893
|
+
document = parseSceneTextFormat(text);
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
if (!(error instanceof SceneTextFormatError))
|
|
1896
|
+
throw error;
|
|
1897
|
+
incomplete.push(`${resourceKey4(resource)}: could not be parsed (${error.message})`);
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
const target = firstField3(document, kind.field);
|
|
1901
|
+
if (target === undefined) {
|
|
1902
|
+
incomplete.push(`${resourceKey4(resource)}: declares no ${kind.field}:, so the collection it names is unclassified`);
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
record(kind, target);
|
|
1906
|
+
}
|
|
1907
|
+
function walk(message, blockName, displayPath, depth) {
|
|
1908
|
+
if (blockName === "components") {
|
|
1909
|
+
const component = firstField3(message, "component");
|
|
1910
|
+
if (component !== undefined)
|
|
1911
|
+
followReferenceDocument(displayPath, component);
|
|
1912
|
+
}
|
|
1913
|
+
const payloads = [];
|
|
1914
|
+
if (EMBEDDING_BLOCKS2.has(blockName)) {
|
|
1915
|
+
for (const payload of message.fields.get("data") ?? []) {
|
|
1916
|
+
try {
|
|
1917
|
+
payloads.push(parseSceneTextFormat(payload));
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
if (!(error instanceof SceneTextFormatError))
|
|
1920
|
+
throw error;
|
|
1921
|
+
incomplete.push(`${displayPath}: embedded payload at depth ${depth + 1} could not be parsed (${error.message})`);
|
|
1922
|
+
}
|
|
934
1923
|
}
|
|
935
1924
|
}
|
|
936
|
-
|
|
937
|
-
const
|
|
938
|
-
const
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1925
|
+
if (blockName === "embedded_components") {
|
|
1926
|
+
const type = firstField3(message, "type");
|
|
1927
|
+
const kind = REFERENCE_KINDS.find((candidate) => candidate.type === type);
|
|
1928
|
+
if (kind !== undefined) {
|
|
1929
|
+
for (const payload of payloads) {
|
|
1930
|
+
const target = firstField3(payload, kind.field);
|
|
1931
|
+
if (target === undefined) {
|
|
1932
|
+
incomplete.push(`${displayPath}: the embedded ${kind.type} declares no ${kind.field}:, so the collection it names is unclassified`);
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
record(kind, target);
|
|
1936
|
+
}
|
|
945
1937
|
}
|
|
946
|
-
|
|
947
|
-
|
|
1938
|
+
}
|
|
1939
|
+
for (const payload of payloads)
|
|
1940
|
+
walk(payload, "", displayPath, depth + 1);
|
|
1941
|
+
for (const [name, nested] of message.messages) {
|
|
1942
|
+
for (const child of nested)
|
|
1943
|
+
walk(child, name, displayPath, depth);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
for (const [displayPath, document] of parsed)
|
|
1947
|
+
walk(document, "", displayPath, 0);
|
|
1948
|
+
const sockets = new Map;
|
|
1949
|
+
const byName = new Map;
|
|
1950
|
+
for (const displayPath of [...proxied].sort()) {
|
|
1951
|
+
const document = parsed.get(displayPath);
|
|
1952
|
+
if (document === undefined) {
|
|
1953
|
+
incomplete.push(`${displayPath}: is opened as a collection proxy but is not among the project's readable scene documents, so its world has no name`);
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
const name = firstField3(document, "name");
|
|
1957
|
+
if (name === undefined || name === "") {
|
|
1958
|
+
incomplete.push(`${displayPath}: is opened as a collection proxy but declares no name:, so its world cannot be addressed`);
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
sockets.set(displayPath, name);
|
|
1962
|
+
byName.set(name, [...byName.get(name) ?? [], displayPath]);
|
|
1963
|
+
}
|
|
1964
|
+
for (const [name, holders] of byName) {
|
|
1965
|
+
if (holders.length > 1) {
|
|
1966
|
+
incomplete.push(`${holders.join(", ")}: all declare name: "${name}", so an address under that socket does not say which world it means`);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
const hasCollections = [...parsed.keys()].some(isCollection);
|
|
1970
|
+
let bootstrap;
|
|
1971
|
+
if (gameProject === undefined) {
|
|
1972
|
+
if (hasCollections) {
|
|
1973
|
+
incomplete.push("game.project could not be read, so no collection is known to be the bootstrap world and no address is offered unqualified");
|
|
1974
|
+
}
|
|
1975
|
+
} else {
|
|
1976
|
+
const main = readGameProjectSetting(gameProject, "bootstrap", "main_collection");
|
|
1977
|
+
if (main === undefined) {
|
|
1978
|
+
if (hasCollections) {
|
|
1979
|
+
incomplete.push("game.project declares no [bootstrap] main_collection, so no collection is the bootstrap world");
|
|
948
1980
|
}
|
|
1981
|
+
} else if (!parsed.has(collectionKey(main))) {
|
|
1982
|
+
incomplete.push(`game.project names ${main} as [bootstrap] main_collection, which is not among the project's readable scene documents`);
|
|
1983
|
+
} else {
|
|
1984
|
+
bootstrap = collectionKey(main);
|
|
949
1985
|
}
|
|
950
|
-
inProgress.delete(displayPath);
|
|
951
|
-
composed.set(displayPath, paths2);
|
|
952
|
-
return paths2;
|
|
953
1986
|
}
|
|
954
|
-
const paths = new Set;
|
|
955
|
-
const declaredIn = new Map;
|
|
956
1987
|
for (const displayPath of parsed.keys()) {
|
|
957
|
-
|
|
958
|
-
if (instanced.has(displayPath))
|
|
1988
|
+
if (!isCollection(displayPath))
|
|
959
1989
|
continue;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1990
|
+
if (displayPath === bootstrap || sockets.has(displayPath) || instanced.has(displayPath) || factoryPrototypes.has(displayPath)) {
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
incomplete.push(`${displayPath}: is reached by no bootstrap, proxy, instance or factory reference, so which world its objects live in is unknown`);
|
|
1994
|
+
}
|
|
1995
|
+
return { bootstrap, sockets, instanced, factoryPrototypes, incomplete };
|
|
1996
|
+
}
|
|
1997
|
+
// src/scene-naming-context.ts
|
|
1998
|
+
var SCRIPT_SUFFIX4 = ".script";
|
|
1999
|
+
var GUI_SUFFIX = ".gui";
|
|
2000
|
+
function socketOfAddress(address) {
|
|
2001
|
+
const pathStart = address.indexOf("/");
|
|
2002
|
+
const fragmentStart = address.indexOf("#");
|
|
2003
|
+
const ends = [pathStart, fragmentStart, address.length].filter((end) => end >= 0);
|
|
2004
|
+
const headEnd = Math.min(...ends);
|
|
2005
|
+
const delimiter = address.indexOf(":");
|
|
2006
|
+
return delimiter > 0 && delimiter < headEnd ? address.slice(0, delimiter) : undefined;
|
|
2007
|
+
}
|
|
2008
|
+
function contextOf(object) {
|
|
2009
|
+
return {
|
|
2010
|
+
object,
|
|
2011
|
+
socket: socketOfAddress(object),
|
|
2012
|
+
prefix: object.slice(0, object.lastIndexOf("/") + 1)
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function buildScriptNamingContexts(index, guiScripts) {
|
|
2016
|
+
const contexts = new Map;
|
|
2017
|
+
for (const [object, resources] of index.componentResourcesOf) {
|
|
2018
|
+
for (const resource of resources.values()) {
|
|
2019
|
+
const script = resource.endsWith(SCRIPT_SUFFIX4) ? resource : resource.endsWith(GUI_SUFFIX) ? guiScripts.get(resource) : undefined;
|
|
2020
|
+
if (script === undefined)
|
|
2021
|
+
continue;
|
|
2022
|
+
const hosted = contexts.get(script);
|
|
2023
|
+
if (hosted === undefined) {
|
|
2024
|
+
contexts.set(script, [contextOf(object)]);
|
|
2025
|
+
} else if (!hosted.some((context) => context.object === object)) {
|
|
2026
|
+
hosted.push(contextOf(object));
|
|
967
2027
|
}
|
|
968
2028
|
}
|
|
969
2029
|
}
|
|
970
|
-
for (const
|
|
971
|
-
|
|
972
|
-
|
|
2030
|
+
for (const hosted of contexts.values()) {
|
|
2031
|
+
hosted.sort((a, b) => a.object.localeCompare(b.object));
|
|
2032
|
+
}
|
|
2033
|
+
return contexts;
|
|
2034
|
+
}
|
|
2035
|
+
function relativeAddressesFrom(index, context) {
|
|
2036
|
+
const paths = new Set;
|
|
2037
|
+
const addresses = new Set;
|
|
2038
|
+
for (const key of index.paths) {
|
|
2039
|
+
if (!key.startsWith(context.prefix))
|
|
2040
|
+
continue;
|
|
2041
|
+
const relative = key.slice(context.prefix.length);
|
|
2042
|
+
if (relative === "")
|
|
2043
|
+
continue;
|
|
2044
|
+
paths.add(relative);
|
|
2045
|
+
for (const id of index.componentsOf.get(key) ?? []) {
|
|
2046
|
+
addresses.add(`${relative}#${id}`);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
return { paths, addresses };
|
|
973
2050
|
}
|
|
974
2051
|
// src/session.ts
|
|
975
2052
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
@@ -1396,179 +2473,6 @@ function createTranspileSession() {
|
|
|
1396
2473
|
getProgram: () => program
|
|
1397
2474
|
};
|
|
1398
2475
|
}
|
|
1399
|
-
// src/sprite-animation-index.ts
|
|
1400
|
-
function resourceKey2(resource) {
|
|
1401
|
-
return resource.startsWith("/") ? resource.slice(1) : resource;
|
|
1402
|
-
}
|
|
1403
|
-
function normalizeDocumentKey(path4) {
|
|
1404
|
-
return path4.replaceAll("\\", "/");
|
|
1405
|
-
}
|
|
1406
|
-
var SCRIPT_SUFFIX2 = ".script";
|
|
1407
|
-
var SPRITE_SUFFIX = ".sprite";
|
|
1408
|
-
function firstField2(message, name) {
|
|
1409
|
-
return (message.fields.get(name) ?? [])[0];
|
|
1410
|
-
}
|
|
1411
|
-
function childrenOf2(message, name) {
|
|
1412
|
-
return message.messages.get(name) ?? [];
|
|
1413
|
-
}
|
|
1414
|
-
function declaredAnimations(document) {
|
|
1415
|
-
const ids = new Set;
|
|
1416
|
-
for (const animation of childrenOf2(document, "animations")) {
|
|
1417
|
-
for (const id of animation.fields.get("id") ?? []) {
|
|
1418
|
-
if (id !== "")
|
|
1419
|
-
ids.add(id);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
return ids;
|
|
1423
|
-
}
|
|
1424
|
-
function readAssets(assets, unresolved) {
|
|
1425
|
-
const animationsByTileSet = new Map;
|
|
1426
|
-
const tileSetBySprite = new Map;
|
|
1427
|
-
for (const [path4, text] of assets) {
|
|
1428
|
-
const displayPath = normalizeDocumentKey(path4);
|
|
1429
|
-
let document;
|
|
1430
|
-
try {
|
|
1431
|
-
document = parseSceneTextFormat(text);
|
|
1432
|
-
} catch (error) {
|
|
1433
|
-
if (!(error instanceof SceneTextFormatError))
|
|
1434
|
-
throw error;
|
|
1435
|
-
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
1436
|
-
continue;
|
|
1437
|
-
}
|
|
1438
|
-
if (displayPath.endsWith(SPRITE_SUFFIX)) {
|
|
1439
|
-
const tileSet = firstField2(document, "tile_set");
|
|
1440
|
-
if (tileSet !== undefined)
|
|
1441
|
-
tileSetBySprite.set(displayPath, resourceKey2(tileSet));
|
|
1442
|
-
continue;
|
|
1443
|
-
}
|
|
1444
|
-
animationsByTileSet.set(displayPath, declaredAnimations(document));
|
|
1445
|
-
}
|
|
1446
|
-
return { animationsByTileSet, tileSetBySprite };
|
|
1447
|
-
}
|
|
1448
|
-
function spriteTileSets(object, displayPath, assets, unresolved) {
|
|
1449
|
-
const tileSets = new Map;
|
|
1450
|
-
for (const embedded of childrenOf2(object, "embedded_components")) {
|
|
1451
|
-
if (firstField2(embedded, "type") !== "sprite")
|
|
1452
|
-
continue;
|
|
1453
|
-
const id = firstField2(embedded, "id");
|
|
1454
|
-
const payload = firstField2(embedded, "data");
|
|
1455
|
-
if (id === undefined || payload === undefined)
|
|
1456
|
-
continue;
|
|
1457
|
-
try {
|
|
1458
|
-
const tileSet = firstField2(parseSceneTextFormat(payload), "tile_set");
|
|
1459
|
-
if (tileSet !== undefined)
|
|
1460
|
-
tileSets.set(id, resourceKey2(tileSet));
|
|
1461
|
-
} catch (error) {
|
|
1462
|
-
if (!(error instanceof SceneTextFormatError))
|
|
1463
|
-
throw error;
|
|
1464
|
-
unresolved.push(`${displayPath}: the embedded sprite "${id}" could not be read (${error.message})`);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
for (const referenced of childrenOf2(object, "components")) {
|
|
1468
|
-
const component = firstField2(referenced, "component");
|
|
1469
|
-
const id = firstField2(referenced, "id");
|
|
1470
|
-
if (id === undefined || component === undefined || !component.endsWith(SPRITE_SUFFIX))
|
|
1471
|
-
continue;
|
|
1472
|
-
const tileSet = assets.tileSetBySprite.get(resourceKey2(component));
|
|
1473
|
-
if (tileSet === undefined) {
|
|
1474
|
-
unresolved.push(`${displayPath}: the sprite component "${id}" names ${component}, which is not among the project's sprite documents`);
|
|
1475
|
-
continue;
|
|
1476
|
-
}
|
|
1477
|
-
tileSets.set(id, tileSet);
|
|
1478
|
-
}
|
|
1479
|
-
return tileSets;
|
|
1480
|
-
}
|
|
1481
|
-
function scriptResourcesOf(object) {
|
|
1482
|
-
const resources = [];
|
|
1483
|
-
for (const component of childrenOf2(object, "components")) {
|
|
1484
|
-
const resource = firstField2(component, "component");
|
|
1485
|
-
if (resource === undefined || !resource.endsWith(SCRIPT_SUFFIX2))
|
|
1486
|
-
continue;
|
|
1487
|
-
const key = resourceKey2(resource);
|
|
1488
|
-
if (key !== SCRIPT_SUFFIX2.slice(1))
|
|
1489
|
-
resources.push(key);
|
|
1490
|
-
}
|
|
1491
|
-
return resources;
|
|
1492
|
-
}
|
|
1493
|
-
function isGameObject(message) {
|
|
1494
|
-
return message.messages.has("components") || message.messages.has("embedded_components");
|
|
1495
|
-
}
|
|
1496
|
-
function buildSpriteAnimationIndex(input) {
|
|
1497
|
-
const unresolved = [];
|
|
1498
|
-
const assets = readAssets(input.assets, unresolved);
|
|
1499
|
-
const byScriptResource = new Map;
|
|
1500
|
-
const tileSetByScriptResource = new Map;
|
|
1501
|
-
const claimedBy = new Map;
|
|
1502
|
-
function walk(message, blockName, displayPath) {
|
|
1503
|
-
if (isGameObject(message)) {
|
|
1504
|
-
claim(message, displayPath);
|
|
1505
|
-
}
|
|
1506
|
-
if (blockName === "embedded_instances") {
|
|
1507
|
-
for (const payload of message.fields.get("data") ?? []) {
|
|
1508
|
-
try {
|
|
1509
|
-
walk(parseSceneTextFormat(payload), "", displayPath);
|
|
1510
|
-
} catch (error) {
|
|
1511
|
-
if (!(error instanceof SceneTextFormatError))
|
|
1512
|
-
throw error;
|
|
1513
|
-
unresolved.push(`${displayPath}: an embedded game object could not be read (${error.message})`);
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
for (const [name, nested] of message.messages) {
|
|
1518
|
-
for (const child of nested) {
|
|
1519
|
-
walk(child, name, displayPath);
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
function claim(object, displayPath) {
|
|
1524
|
-
const scripts = scriptResourcesOf(object);
|
|
1525
|
-
if (scripts.length === 0)
|
|
1526
|
-
return;
|
|
1527
|
-
const tileSets = spriteTileSets(object, displayPath, assets, unresolved);
|
|
1528
|
-
const animations = new Map;
|
|
1529
|
-
const declaringTileSets = new Map;
|
|
1530
|
-
for (const [id, tileSet] of tileSets) {
|
|
1531
|
-
const declared = assets.animationsByTileSet.get(tileSet);
|
|
1532
|
-
if (declared === undefined) {
|
|
1533
|
-
unresolved.push(`${displayPath}: the sprite component "${id}" names the tile source /${tileSet}, which is not among the project's asset documents`);
|
|
1534
|
-
continue;
|
|
1535
|
-
}
|
|
1536
|
-
animations.set(id, declared);
|
|
1537
|
-
declaringTileSets.set(id, tileSet);
|
|
1538
|
-
}
|
|
1539
|
-
for (const key of scripts) {
|
|
1540
|
-
const owner = claimedBy.get(key);
|
|
1541
|
-
if (owner !== undefined) {
|
|
1542
|
-
byScriptResource.delete(key);
|
|
1543
|
-
tileSetByScriptResource.delete(key);
|
|
1544
|
-
unresolved.push(`${key}: claimed by both ${owner} and ${displayPath}, so its sprite animations are ambiguous`);
|
|
1545
|
-
continue;
|
|
1546
|
-
}
|
|
1547
|
-
claimedBy.set(key, displayPath);
|
|
1548
|
-
byScriptResource.set(key, animations);
|
|
1549
|
-
tileSetByScriptResource.set(key, declaringTileSets);
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
for (const [path4, text] of input.scenes) {
|
|
1553
|
-
const displayPath = normalizeDocumentKey(path4);
|
|
1554
|
-
try {
|
|
1555
|
-
walk(parseSceneTextFormat(text), "", displayPath);
|
|
1556
|
-
} catch (error) {
|
|
1557
|
-
if (!(error instanceof SceneTextFormatError))
|
|
1558
|
-
throw error;
|
|
1559
|
-
unresolved.push(`${displayPath}: could not be parsed (${error.message})`);
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
return { byScriptResource, tileSetByScriptResource, unresolved };
|
|
1563
|
-
}
|
|
1564
|
-
function componentIdOfSameObjectAddress(address) {
|
|
1565
|
-
if (!address.startsWith("#"))
|
|
1566
|
-
return;
|
|
1567
|
-
const id = address.slice(1);
|
|
1568
|
-
if (id === "" || id.includes("/") || id.includes("#"))
|
|
1569
|
-
return;
|
|
1570
|
-
return id;
|
|
1571
|
-
}
|
|
1572
2476
|
// src/url-address-slots.ts
|
|
1573
2477
|
import * as ts11 from "typescript";
|
|
1574
2478
|
var ADDRESS_CLASSES = {
|
|
@@ -1660,6 +2564,10 @@ function slotThroughHashedCall(checker, table, literal) {
|
|
|
1660
2564
|
const ascended = classifiedEntryOfArgument(checker, table, call);
|
|
1661
2565
|
return ascended?.entry.fqn === "hash" ? undefined : ascended;
|
|
1662
2566
|
}
|
|
2567
|
+
function addressEntryOfArgument(checker, table, argument) {
|
|
2568
|
+
const entry = classifiedEntryOfArgument(checker, table, argument)?.entry;
|
|
2569
|
+
return entry !== undefined && isAddressClass(entry.class) ? entry : undefined;
|
|
2570
|
+
}
|
|
1663
2571
|
function addressClassOfArgument(checker, table, argument) {
|
|
1664
2572
|
return classifiedEntryOfArgument(checker, table, argument)?.entry.class ?? "none";
|
|
1665
2573
|
}
|
|
@@ -1713,10 +2621,14 @@ function resolveClassifiedSlotAtPosition(input) {
|
|
|
1713
2621
|
textStart,
|
|
1714
2622
|
fragmentStart: hash === -1 ? -1 : textStart + hash + 1,
|
|
1715
2623
|
...addressText === undefined ? {} : { addressText },
|
|
2624
|
+
...classified.entry.nodeParameter === undefined ? {} : { nodeParameter: classified.entry.nodeParameter },
|
|
1716
2625
|
...classified.entry.resourceExtensions === undefined ? {} : { resourceExtensions: classified.entry.resourceExtensions }
|
|
1717
2626
|
};
|
|
1718
2627
|
}
|
|
1719
|
-
// src/url-
|
|
2628
|
+
// src/url-cross-world-address.ts
|
|
2629
|
+
import * as ts13 from "typescript";
|
|
2630
|
+
|
|
2631
|
+
// src/url-address-literals.ts
|
|
1720
2632
|
import * as ts12 from "typescript";
|
|
1721
2633
|
function isAmbient2(fileName) {
|
|
1722
2634
|
return /[\\/]node_modules[\\/]/.test(fileName) || /(^|[\\/])lib\.[^\\/]*\.d\.ts$/.test(fileName);
|
|
@@ -1767,8 +2679,89 @@ function hashedSourceOfType(checker, type, canonical) {
|
|
|
1767
2679
|
const [source] = checker.getTypeArguments(type);
|
|
1768
2680
|
return source?.isStringLiteral() ? source.value : undefined;
|
|
1769
2681
|
}
|
|
2682
|
+
function staticAddressTextOf(checker, node, canonical) {
|
|
2683
|
+
return ts12.isStringLiteralLike(node) ? node.text : hashedSourceOfType(checker, checker.getTypeAtLocation(node), canonical);
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
// src/url-cross-world-address.ts
|
|
2687
|
+
function slotRejectsForeignSocket(entry) {
|
|
2688
|
+
return entry.socketScope === "same-world";
|
|
2689
|
+
}
|
|
2690
|
+
function worldPhrase(sockets) {
|
|
2691
|
+
const named = [...new Set(sockets)].map((socket) => socket === undefined ? "the bootstrap world" : `"${socket}"`);
|
|
2692
|
+
return named.join(" and ");
|
|
2693
|
+
}
|
|
2694
|
+
function checkCrossWorldAddresses(input) {
|
|
2695
|
+
const { program, table, worldsOf, sourceFiles } = input;
|
|
2696
|
+
const checker = program.getTypeChecker();
|
|
2697
|
+
const canonical = canonicalHashSymbols(checker, program);
|
|
2698
|
+
const findings = [];
|
|
2699
|
+
const targets = sourceFiles ?? program.getSourceFiles();
|
|
2700
|
+
for (const sourceFile of targets) {
|
|
2701
|
+
if (isAmbient2(sourceFile.fileName))
|
|
2702
|
+
continue;
|
|
2703
|
+
const worlds = worldsOf(sourceFile.fileName);
|
|
2704
|
+
if (worlds.length === 0)
|
|
2705
|
+
continue;
|
|
2706
|
+
const visit = (node) => {
|
|
2707
|
+
if (ts13.isExpression(node)) {
|
|
2708
|
+
const entry = addressEntryOfArgument(checker, table, node);
|
|
2709
|
+
if (entry !== undefined && slotRejectsForeignSocket(entry)) {
|
|
2710
|
+
const address = staticAddressTextOf(checker, node, canonical);
|
|
2711
|
+
const socket = address === undefined ? undefined : socketOfAddress(address);
|
|
2712
|
+
if (address !== undefined && socket !== undefined && worlds.every((world) => world !== socket)) {
|
|
2713
|
+
findings.push({
|
|
2714
|
+
fileName: sourceFile.fileName,
|
|
2715
|
+
start: node.getStart(sourceFile),
|
|
2716
|
+
length: node.getWidth(sourceFile),
|
|
2717
|
+
address,
|
|
2718
|
+
socket,
|
|
2719
|
+
message: `this address names the world "${socket}", but \`${entry.fqn}\` resolves it in ` + `the world the script runs in (${worldPhrase(worlds)}), so it cannot resolve at ` + `runtime — only \`msg.post\` and \`msg.url\` cross a collection proxy`
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
ts13.forEachChild(node, visit);
|
|
2725
|
+
};
|
|
2726
|
+
ts13.forEachChild(sourceFile, visit);
|
|
2727
|
+
}
|
|
2728
|
+
return findings;
|
|
2729
|
+
}
|
|
2730
|
+
// src/url-fragment-reachability.ts
|
|
2731
|
+
import * as ts14 from "typescript";
|
|
2732
|
+
function pathOfKey(key) {
|
|
2733
|
+
const socket = socketOfAddress(key);
|
|
2734
|
+
return socket === undefined ? key : key.slice(socket.length + 1);
|
|
2735
|
+
}
|
|
2736
|
+
function declaredComponentsOf(objects, path4) {
|
|
2737
|
+
if (objects === undefined || path4 === "")
|
|
2738
|
+
return;
|
|
2739
|
+
if (objects.incomplete.length > 0)
|
|
2740
|
+
return;
|
|
2741
|
+
if (socketOfAddress(path4) !== undefined) {
|
|
2742
|
+
return objects.paths.has(path4) ? objects.componentsOf.get(path4) : undefined;
|
|
2743
|
+
}
|
|
2744
|
+
if (!path4.startsWith("/"))
|
|
2745
|
+
return;
|
|
2746
|
+
const declared = new Set;
|
|
2747
|
+
let known = false;
|
|
2748
|
+
for (const key of objects.paths) {
|
|
2749
|
+
if (pathOfKey(key) !== path4)
|
|
2750
|
+
continue;
|
|
2751
|
+
const owned = objects.componentsOf.get(key);
|
|
2752
|
+
if (owned === undefined)
|
|
2753
|
+
return;
|
|
2754
|
+
known = true;
|
|
2755
|
+
for (const id of owned)
|
|
2756
|
+
declared.add(id);
|
|
2757
|
+
}
|
|
2758
|
+
return known ? [...declared].sort() : undefined;
|
|
2759
|
+
}
|
|
2760
|
+
function declaredPhrase(ids) {
|
|
2761
|
+
return ids.length === 0 ? "it declares no components at all" : `it declares ${ids.map((id) => `"${id}"`).join(", ")}`;
|
|
2762
|
+
}
|
|
1770
2763
|
function checkUrlFragmentReachability(input) {
|
|
1771
|
-
const { program, table, index, sourceFiles } = input;
|
|
2764
|
+
const { program, table, index, objects, sourceFiles } = input;
|
|
1772
2765
|
if (index.incomplete.length > 0) {
|
|
1773
2766
|
return { kind: "suppressed", reasons: index.incomplete };
|
|
1774
2767
|
}
|
|
@@ -1780,25 +2773,28 @@ function checkUrlFragmentReachability(input) {
|
|
|
1780
2773
|
if (isAmbient2(sourceFile.fileName))
|
|
1781
2774
|
continue;
|
|
1782
2775
|
const visit = (node) => {
|
|
1783
|
-
if (
|
|
1784
|
-
const text =
|
|
2776
|
+
if (ts14.isExpression(node) && isAddressClass(addressClassOfArgument(checker, table, node))) {
|
|
2777
|
+
const text = staticAddressTextOf(checker, node, canonical);
|
|
1785
2778
|
if (text !== undefined) {
|
|
1786
2779
|
const hash = text.indexOf("#");
|
|
1787
2780
|
const fragment = hash === -1 ? "" : text.slice(hash + 1);
|
|
1788
|
-
|
|
2781
|
+
const declared = fragment === "" ? undefined : declaredComponentsOf(objects, text.slice(0, hash));
|
|
2782
|
+
const reachable = declared === undefined ? index.ids.has(fragment) : declared.includes(fragment);
|
|
2783
|
+
if (fragment !== "" && !reachable) {
|
|
2784
|
+
const path4 = text.slice(0, hash);
|
|
1789
2785
|
findings.push({
|
|
1790
2786
|
fileName: sourceFile.fileName,
|
|
1791
2787
|
start: node.getStart(sourceFile),
|
|
1792
2788
|
length: node.getWidth(sourceFile),
|
|
1793
2789
|
fragment,
|
|
1794
|
-
message: `no \`.go\` or \`.collection\` in this project declares a component with the id ` + `"${fragment}", so this address cannot resolve at runtime`
|
|
2790
|
+
message: declared === undefined ? `no \`.go\` or \`.collection\` in this project declares a component with the id ` + `"${fragment}", so this address cannot resolve at runtime` : `the game object "${path4}" declares no component with the id "${fragment}" ` + `(${declaredPhrase(declared)}), so this address cannot resolve at runtime`
|
|
1795
2791
|
});
|
|
1796
2792
|
}
|
|
1797
2793
|
}
|
|
1798
2794
|
}
|
|
1799
|
-
|
|
2795
|
+
ts14.forEachChild(node, visit);
|
|
1800
2796
|
};
|
|
1801
|
-
|
|
2797
|
+
ts14.forEachChild(sourceFile, visit);
|
|
1802
2798
|
}
|
|
1803
2799
|
return { kind: "checked", findings };
|
|
1804
2800
|
}
|
|
@@ -1806,27 +2802,59 @@ export {
|
|
|
1806
2802
|
transpileProject,
|
|
1807
2803
|
transpile,
|
|
1808
2804
|
stripIncludeBase,
|
|
2805
|
+
socketOfAddress,
|
|
2806
|
+
slotRejectsForeignSocket,
|
|
1809
2807
|
resolveClassifiedSlotAtPosition,
|
|
2808
|
+
relativeAddressesFrom,
|
|
2809
|
+
readSceneDocuments,
|
|
2810
|
+
readLibrarySceneDocuments,
|
|
2811
|
+
readGameProjectSetting,
|
|
2812
|
+
readGameProjectDependencies,
|
|
1810
2813
|
parseSceneTextFormat,
|
|
1811
2814
|
parseBuildConfig,
|
|
2815
|
+
listProjectResourcePaths,
|
|
2816
|
+
libraryIncludedEntries,
|
|
1812
2817
|
isFragmentCaret,
|
|
2818
|
+
isExcludedProjectPath,
|
|
1813
2819
|
isDefignoredPath,
|
|
2820
|
+
isContainedResourcePath,
|
|
1814
2821
|
isAddressClass,
|
|
2822
|
+
guiScriptResourcesOf,
|
|
2823
|
+
guiScriptResourceOf,
|
|
1815
2824
|
getProgramDiagnostics,
|
|
1816
2825
|
findMainEntryFactoryImports,
|
|
2826
|
+
displayPathOf,
|
|
1817
2827
|
createTranspileSession,
|
|
1818
2828
|
computeOutputRel,
|
|
1819
2829
|
componentIdOfSameObjectAddress,
|
|
2830
|
+
collectComponentIds,
|
|
2831
|
+
collectComponentDeclarations,
|
|
1820
2832
|
checkUrlFragmentReachability,
|
|
1821
|
-
|
|
2833
|
+
checkCrossWorldAddresses,
|
|
2834
|
+
buildScriptNamingContexts,
|
|
1822
2835
|
buildSceneObjectPathIndex,
|
|
1823
2836
|
buildSceneComponentIndex,
|
|
2837
|
+
buildSceneCollectionRoles,
|
|
2838
|
+
buildSceneAddressDeclaration,
|
|
1824
2839
|
buildInputActionIndex,
|
|
1825
2840
|
buildGuiNodeIndex,
|
|
2841
|
+
buildGuiFlipbookIndex,
|
|
1826
2842
|
buildConfigKeyIndex,
|
|
2843
|
+
buildComponentAnimationIndex,
|
|
2844
|
+
archiveWrapperOf,
|
|
2845
|
+
addressEntryOfArgument,
|
|
1827
2846
|
addressClassOfArgument,
|
|
1828
2847
|
SceneTextFormatError,
|
|
1829
2848
|
SCRIPT_SUFFIX_BY_KIND,
|
|
2849
|
+
SCENE_EXTENSIONS,
|
|
1830
2850
|
SCAFFOLDED_DEFIGNORE_LINES,
|
|
1831
|
-
|
|
2851
|
+
PROJECT_EXTENSIONS,
|
|
2852
|
+
LIBRARY_SCENE_EXTENSIONS,
|
|
2853
|
+
LIBRARY_DEPENDENCY_ROOT,
|
|
2854
|
+
INPUT_BINDING_EXTENSIONS,
|
|
2855
|
+
GUI_EXTENSIONS,
|
|
2856
|
+
GAME_PROJECT_DOCUMENT,
|
|
2857
|
+
DEFAULT_INCLUDE,
|
|
2858
|
+
COLLECTION_REFERENCE_EXTENSIONS,
|
|
2859
|
+
ANIMATION_ASSET_EXTENSIONS
|
|
1832
2860
|
};
|