@defold-typescript/cli 0.5.0 → 0.5.2

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/bin.js CHANGED
@@ -348,6 +348,107 @@ var VSCODE_EXTENSIONS_CONTENT = {
348
348
  var VSCODE_SETTINGS_CONTENT = {
349
349
  "Lua.workspace.ignoreDir": ["src"]
350
350
  };
351
+ function inlineSnippetBody(factory, includeOnInput) {
352
+ const lines = [
353
+ `import { ${factory} } from "@defold-typescript/types";`,
354
+ "",
355
+ `export const script = ${factory}({`,
356
+ " // Initialize the component and return its state.",
357
+ " init() {",
358
+ " return { $0 };",
359
+ " },",
360
+ " // Update the component every frame; `dt` is the time step.",
361
+ " update(self, dt) {$1},",
362
+ " // Update at the fixed physics time step.",
363
+ " fixed_update(self, dt) {$2},",
364
+ " // Update every frame after `update`.",
365
+ " late_update(self, dt) {$3},",
366
+ " // Handle an incoming message.",
367
+ " on_message(self, message_id, message, sender) {$4},"
368
+ ];
369
+ if (includeOnInput) {
370
+ lines.push(" // Handle input once input focus is acquired.");
371
+ lines.push(" on_input(self, action_id, action) {$5},");
372
+ }
373
+ lines.push(" // Clean up when the component is deleted.");
374
+ lines.push(" final(self) {$6},");
375
+ lines.push(" // React to a hot reload of this script.");
376
+ lines.push(" on_reload(self) {$7},");
377
+ lines.push("});");
378
+ return lines;
379
+ }
380
+ function typedSnippetBody(factory, includeOnInput) {
381
+ const lines = [
382
+ `import { ${factory} } from "@defold-typescript/types";`,
383
+ "",
384
+ "type Self = {",
385
+ " // Your script's state goes here.",
386
+ " $1",
387
+ "};",
388
+ "",
389
+ `export const script = ${factory}<Self>({`,
390
+ " // Initialize the component and return its state.",
391
+ " init(): Self {",
392
+ " return { $0 };",
393
+ " },",
394
+ " // Update the component every frame; `dt` is the time step.",
395
+ " update(self, dt) {$2},",
396
+ " // Update at the fixed physics time step.",
397
+ " fixed_update(self, dt) {$3},",
398
+ " // Update every frame after `update`.",
399
+ " late_update(self, dt) {$4},",
400
+ " // Handle an incoming message.",
401
+ " on_message(self, message_id, message, sender) {$5},"
402
+ ];
403
+ if (includeOnInput) {
404
+ lines.push(" // Handle input once input focus is acquired.");
405
+ lines.push(" on_input(self, action_id, action) {$6},");
406
+ }
407
+ lines.push(" // Clean up when the component is deleted.");
408
+ lines.push(" final(self) {$7},");
409
+ lines.push(" // React to a hot reload of this script.");
410
+ lines.push(" on_reload(self) {$8},");
411
+ lines.push("});");
412
+ return lines;
413
+ }
414
+ var VSCODE_SNIPPETS_CONTENT = {
415
+ "Defold script (inferred self)": {
416
+ scope: "typescript",
417
+ prefix: "defold-script",
418
+ body: inlineSnippetBody("defineScript", true),
419
+ description: "Empty Defold script; state inferred from init's return."
420
+ },
421
+ "Defold script (typed self)": {
422
+ scope: "typescript",
423
+ prefix: "defold-script-typed",
424
+ body: typedSnippetBody("defineScript", true),
425
+ description: "Empty Defold script with an explicit Self type."
426
+ },
427
+ "Defold GUI script (inferred self)": {
428
+ scope: "typescript",
429
+ prefix: "defold-gui",
430
+ body: inlineSnippetBody("defineGuiScript", true),
431
+ description: "Empty Defold GUI script; state inferred from init's return."
432
+ },
433
+ "Defold GUI script (typed self)": {
434
+ scope: "typescript",
435
+ prefix: "defold-gui-typed",
436
+ body: typedSnippetBody("defineGuiScript", true),
437
+ description: "Empty Defold GUI script with an explicit Self type."
438
+ },
439
+ "Defold render script (inferred self)": {
440
+ scope: "typescript",
441
+ prefix: "defold-render",
442
+ body: inlineSnippetBody("defineRenderScript", false),
443
+ description: "Empty Defold render script; state inferred from init's return."
444
+ },
445
+ "Defold render script (typed self)": {
446
+ scope: "typescript",
447
+ prefix: "defold-render-typed",
448
+ body: typedSnippetBody("defineRenderScript", false),
449
+ description: "Empty Defold render script with an explicit Self type."
450
+ }
451
+ };
351
452
  var MAIN_TS_CONTENT = `export function init(): void {
352
453
  const start = vmath.vector3(0, 0, 0);
353
454
  msg.post("main:/hero", "spawn", { start });
@@ -522,6 +623,26 @@ function writeVscodeSettings(cwd, written) {
522
623
  writeJson(filePath, VSCODE_SETTINGS_CONTENT);
523
624
  written.push(".vscode/settings.json");
524
625
  }
626
+ function writeVscodeSnippets(cwd, written) {
627
+ const dir = path6.join(cwd, ".vscode");
628
+ const filePath = path6.join(dir, "defold-typescript.code-snippets");
629
+ if (existsSync2(filePath)) {
630
+ const existing = readVscodeJson(filePath);
631
+ if (existing === null) {
632
+ return;
633
+ }
634
+ for (const [key, snippet] of Object.entries(VSCODE_SNIPPETS_CONTENT)) {
635
+ if (!(key in existing)) {
636
+ existing[key] = snippet;
637
+ }
638
+ }
639
+ writeJson(filePath, existing);
640
+ return;
641
+ }
642
+ mkdirSync2(dir, { recursive: true });
643
+ writeJson(filePath, VSCODE_SNIPPETS_CONTENT);
644
+ written.push(".vscode/defold-typescript.code-snippets");
645
+ }
525
646
  function writeTsSurface(cwd, written, force = false) {
526
647
  mkdirSync2(path6.join(cwd, "src"), { recursive: true });
527
648
  writeFileSync2(path6.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
@@ -565,6 +686,7 @@ function writeTsSurface(cwd, written, force = false) {
565
686
  writeBiome(cwd, written);
566
687
  writeVscodeExtensions(cwd, written);
567
688
  writeVscodeSettings(cwd, written);
689
+ writeVscodeSnippets(cwd, written);
568
690
  return selectScriptKind(kinds);
569
691
  }
570
692
  function runNewProjectInit(cwd, force = false) {
@@ -650,6 +772,8 @@ function materializeApiSurface(opts) {
650
772
  const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path7.join(srcDir, file)));
651
773
  const coreTypesSrc = path7.join(srcDir, "core-types.ts");
652
774
  const includeCoreTypes = overloads.length > 0 && existsSync3(coreTypesSrc);
775
+ const engineGlobalsSrc = path7.join(srcDir, "engine-globals.d.ts");
776
+ const includeEngineGlobals = includeCoreTypes && existsSync3(engineGlobalsSrc);
653
777
  const wanted = new Set(sources);
654
778
  for (const file of overloads) {
655
779
  wanted.add(file);
@@ -657,6 +781,9 @@ function materializeApiSurface(opts) {
657
781
  if (includeCoreTypes) {
658
782
  wanted.add("core-types.d.ts");
659
783
  }
784
+ if (includeEngineGlobals) {
785
+ wanted.add("engine-globals.d.ts");
786
+ }
660
787
  for (const existing of readdirSync2(absDir)) {
661
788
  if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
662
789
  rmSync(path7.join(absDir, existing));
@@ -668,10 +795,16 @@ function materializeApiSurface(opts) {
668
795
  if (includeCoreTypes) {
669
796
  writeFileSync3(path7.join(absDir, "core-types.d.ts"), readFileSync6(coreTypesSrc, "utf8"));
670
797
  }
798
+ if (includeEngineGlobals) {
799
+ writeFileSync3(path7.join(absDir, "engine-globals.d.ts"), readFileSync6(engineGlobalsSrc, "utf8"));
800
+ }
671
801
  for (const file of overloads) {
672
802
  writeFileSync3(path7.join(absDir, file), readFileSync6(path7.join(srcDir, file), "utf8"));
673
803
  }
674
804
  const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
805
+ if (includeEngineGlobals) {
806
+ modules.push("engine-globals");
807
+ }
675
808
  const imports = modules.map((mod) => `import "./${mod}";`).join(`
676
809
  `);
677
810
  writeFileSync3(path7.join(absDir, "index.d.ts"), `${imports}
@@ -747,6 +880,10 @@ async function materializeRefDocSurface(opts) {
747
880
  ...excludeModules.length > 0 ? { excludeModules } : {}
748
881
  });
749
882
  copyFileSync(path7.join(root, "src", "core-types.ts"), path7.join(absDir, "core-types.d.ts"));
883
+ copyFileSync(path7.join(root, "src", "engine-globals.d.ts"), path7.join(absDir, "engine-globals.d.ts"));
884
+ const indexPath = path7.join(absDir, "index.d.ts");
885
+ writeFileSync3(indexPath, `import "./engine-globals";
886
+ ${readFileSync6(indexPath, "utf8")}`);
750
887
  } catch {
751
888
  rmSync(absDir, { recursive: true, force: true });
752
889
  return { materializedDir: null, active: null };
package/dist/index.js CHANGED
@@ -412,6 +412,107 @@ var VSCODE_EXTENSIONS_CONTENT = {
412
412
  var VSCODE_SETTINGS_CONTENT = {
413
413
  "Lua.workspace.ignoreDir": ["src"]
414
414
  };
415
+ function inlineSnippetBody(factory, includeOnInput) {
416
+ const lines = [
417
+ `import { ${factory} } from "@defold-typescript/types";`,
418
+ "",
419
+ `export const script = ${factory}({`,
420
+ " // Initialize the component and return its state.",
421
+ " init() {",
422
+ " return { $0 };",
423
+ " },",
424
+ " // Update the component every frame; `dt` is the time step.",
425
+ " update(self, dt) {$1},",
426
+ " // Update at the fixed physics time step.",
427
+ " fixed_update(self, dt) {$2},",
428
+ " // Update every frame after `update`.",
429
+ " late_update(self, dt) {$3},",
430
+ " // Handle an incoming message.",
431
+ " on_message(self, message_id, message, sender) {$4},"
432
+ ];
433
+ if (includeOnInput) {
434
+ lines.push(" // Handle input once input focus is acquired.");
435
+ lines.push(" on_input(self, action_id, action) {$5},");
436
+ }
437
+ lines.push(" // Clean up when the component is deleted.");
438
+ lines.push(" final(self) {$6},");
439
+ lines.push(" // React to a hot reload of this script.");
440
+ lines.push(" on_reload(self) {$7},");
441
+ lines.push("});");
442
+ return lines;
443
+ }
444
+ function typedSnippetBody(factory, includeOnInput) {
445
+ const lines = [
446
+ `import { ${factory} } from "@defold-typescript/types";`,
447
+ "",
448
+ "type Self = {",
449
+ " // Your script's state goes here.",
450
+ " $1",
451
+ "};",
452
+ "",
453
+ `export const script = ${factory}<Self>({`,
454
+ " // Initialize the component and return its state.",
455
+ " init(): Self {",
456
+ " return { $0 };",
457
+ " },",
458
+ " // Update the component every frame; `dt` is the time step.",
459
+ " update(self, dt) {$2},",
460
+ " // Update at the fixed physics time step.",
461
+ " fixed_update(self, dt) {$3},",
462
+ " // Update every frame after `update`.",
463
+ " late_update(self, dt) {$4},",
464
+ " // Handle an incoming message.",
465
+ " on_message(self, message_id, message, sender) {$5},"
466
+ ];
467
+ if (includeOnInput) {
468
+ lines.push(" // Handle input once input focus is acquired.");
469
+ lines.push(" on_input(self, action_id, action) {$6},");
470
+ }
471
+ lines.push(" // Clean up when the component is deleted.");
472
+ lines.push(" final(self) {$7},");
473
+ lines.push(" // React to a hot reload of this script.");
474
+ lines.push(" on_reload(self) {$8},");
475
+ lines.push("});");
476
+ return lines;
477
+ }
478
+ var VSCODE_SNIPPETS_CONTENT = {
479
+ "Defold script (inferred self)": {
480
+ scope: "typescript",
481
+ prefix: "defold-script",
482
+ body: inlineSnippetBody("defineScript", true),
483
+ description: "Empty Defold script; state inferred from init's return."
484
+ },
485
+ "Defold script (typed self)": {
486
+ scope: "typescript",
487
+ prefix: "defold-script-typed",
488
+ body: typedSnippetBody("defineScript", true),
489
+ description: "Empty Defold script with an explicit Self type."
490
+ },
491
+ "Defold GUI script (inferred self)": {
492
+ scope: "typescript",
493
+ prefix: "defold-gui",
494
+ body: inlineSnippetBody("defineGuiScript", true),
495
+ description: "Empty Defold GUI script; state inferred from init's return."
496
+ },
497
+ "Defold GUI script (typed self)": {
498
+ scope: "typescript",
499
+ prefix: "defold-gui-typed",
500
+ body: typedSnippetBody("defineGuiScript", true),
501
+ description: "Empty Defold GUI script with an explicit Self type."
502
+ },
503
+ "Defold render script (inferred self)": {
504
+ scope: "typescript",
505
+ prefix: "defold-render",
506
+ body: inlineSnippetBody("defineRenderScript", false),
507
+ description: "Empty Defold render script; state inferred from init's return."
508
+ },
509
+ "Defold render script (typed self)": {
510
+ scope: "typescript",
511
+ prefix: "defold-render-typed",
512
+ body: typedSnippetBody("defineRenderScript", false),
513
+ description: "Empty Defold render script with an explicit Self type."
514
+ }
515
+ };
415
516
  var MAIN_TS_CONTENT = `export function init(): void {
416
517
  const start = vmath.vector3(0, 0, 0);
417
518
  msg.post("main:/hero", "spawn", { start });
@@ -586,6 +687,26 @@ function writeVscodeSettings(cwd, written) {
586
687
  writeJson(filePath, VSCODE_SETTINGS_CONTENT);
587
688
  written.push(".vscode/settings.json");
588
689
  }
690
+ function writeVscodeSnippets(cwd, written) {
691
+ const dir = path7.join(cwd, ".vscode");
692
+ const filePath = path7.join(dir, "defold-typescript.code-snippets");
693
+ if (existsSync2(filePath)) {
694
+ const existing = readVscodeJson(filePath);
695
+ if (existing === null) {
696
+ return;
697
+ }
698
+ for (const [key, snippet] of Object.entries(VSCODE_SNIPPETS_CONTENT)) {
699
+ if (!(key in existing)) {
700
+ existing[key] = snippet;
701
+ }
702
+ }
703
+ writeJson(filePath, existing);
704
+ return;
705
+ }
706
+ mkdirSync2(dir, { recursive: true });
707
+ writeJson(filePath, VSCODE_SNIPPETS_CONTENT);
708
+ written.push(".vscode/defold-typescript.code-snippets");
709
+ }
589
710
  function writeTsSurface(cwd, written, force = false) {
590
711
  mkdirSync2(path7.join(cwd, "src"), { recursive: true });
591
712
  writeFileSync2(path7.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
@@ -629,6 +750,7 @@ function writeTsSurface(cwd, written, force = false) {
629
750
  writeBiome(cwd, written);
630
751
  writeVscodeExtensions(cwd, written);
631
752
  writeVscodeSettings(cwd, written);
753
+ writeVscodeSnippets(cwd, written);
632
754
  return selectScriptKind(kinds);
633
755
  }
634
756
  function runNewProjectInit(cwd, force = false) {
@@ -714,6 +836,8 @@ function materializeApiSurface(opts) {
714
836
  const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path8.join(srcDir, file)));
715
837
  const coreTypesSrc = path8.join(srcDir, "core-types.ts");
716
838
  const includeCoreTypes = overloads.length > 0 && existsSync3(coreTypesSrc);
839
+ const engineGlobalsSrc = path8.join(srcDir, "engine-globals.d.ts");
840
+ const includeEngineGlobals = includeCoreTypes && existsSync3(engineGlobalsSrc);
717
841
  const wanted = new Set(sources);
718
842
  for (const file of overloads) {
719
843
  wanted.add(file);
@@ -721,6 +845,9 @@ function materializeApiSurface(opts) {
721
845
  if (includeCoreTypes) {
722
846
  wanted.add("core-types.d.ts");
723
847
  }
848
+ if (includeEngineGlobals) {
849
+ wanted.add("engine-globals.d.ts");
850
+ }
724
851
  for (const existing of readdirSync2(absDir)) {
725
852
  if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
726
853
  rmSync2(path8.join(absDir, existing));
@@ -732,10 +859,16 @@ function materializeApiSurface(opts) {
732
859
  if (includeCoreTypes) {
733
860
  writeFileSync3(path8.join(absDir, "core-types.d.ts"), readFileSync7(coreTypesSrc, "utf8"));
734
861
  }
862
+ if (includeEngineGlobals) {
863
+ writeFileSync3(path8.join(absDir, "engine-globals.d.ts"), readFileSync7(engineGlobalsSrc, "utf8"));
864
+ }
735
865
  for (const file of overloads) {
736
866
  writeFileSync3(path8.join(absDir, file), readFileSync7(path8.join(srcDir, file), "utf8"));
737
867
  }
738
868
  const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
869
+ if (includeEngineGlobals) {
870
+ modules.push("engine-globals");
871
+ }
739
872
  const imports = modules.map((mod) => `import "./${mod}";`).join(`
740
873
  `);
741
874
  writeFileSync3(path8.join(absDir, "index.d.ts"), `${imports}
@@ -811,6 +944,10 @@ async function materializeRefDocSurface(opts) {
811
944
  ...excludeModules.length > 0 ? { excludeModules } : {}
812
945
  });
813
946
  copyFileSync(path8.join(root, "src", "core-types.ts"), path8.join(absDir, "core-types.d.ts"));
947
+ copyFileSync(path8.join(root, "src", "engine-globals.d.ts"), path8.join(absDir, "engine-globals.d.ts"));
948
+ const indexPath = path8.join(absDir, "index.d.ts");
949
+ writeFileSync3(indexPath, `import "./engine-globals";
950
+ ${readFileSync7(indexPath, "utf8")}`);
814
951
  } catch {
815
952
  rmSync2(absDir, { recursive: true, force: true });
816
953
  return { materializedDir: null, active: null };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defold-typescript/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "End-user CLI for scaffolding and building Defold projects written in TypeScript.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@defold-typescript/transpiler": "0.5.0",
35
- "@defold-typescript/types": "0.5.0"
34
+ "@defold-typescript/transpiler": "0.5.2",
35
+ "@defold-typescript/types": "0.5.2"
36
36
  }
37
37
  }
package/src/init.ts CHANGED
@@ -81,6 +81,122 @@ const VSCODE_SETTINGS_CONTENT = {
81
81
  "Lua.workspace.ignoreDir": ["src"],
82
82
  };
83
83
 
84
+ interface VscodeSnippet {
85
+ scope: string;
86
+ prefix: string;
87
+ body: string[];
88
+ description: string;
89
+ }
90
+
91
+ // Whole-file TS scaffolds mirroring the Defold editor's empty script/gui/render
92
+ // templates over the lifecycle factories. Two self-typing variants per kind:
93
+ // inline-self (TSelf inferred from `init`'s return) and typed-self (an explicit
94
+ // dummy `Self` placeholder). Hook order mirrors the Lua templates; render omits
95
+ // `on_input` because `RenderScriptHooks` does. The final `$0` lands inside `init`.
96
+ function inlineSnippetBody(factory: string, includeOnInput: boolean): string[] {
97
+ const lines = [
98
+ `import { ${factory} } from "@defold-typescript/types";`,
99
+ "",
100
+ `export const script = ${factory}({`,
101
+ " // Initialize the component and return its state.",
102
+ " init() {",
103
+ " return { $0 };",
104
+ " },",
105
+ " // Update the component every frame; `dt` is the time step.",
106
+ " update(self, dt) {$1},",
107
+ " // Update at the fixed physics time step.",
108
+ " fixed_update(self, dt) {$2},",
109
+ " // Update every frame after `update`.",
110
+ " late_update(self, dt) {$3},",
111
+ " // Handle an incoming message.",
112
+ " on_message(self, message_id, message, sender) {$4},",
113
+ ];
114
+ if (includeOnInput) {
115
+ lines.push(" // Handle input once input focus is acquired.");
116
+ lines.push(" on_input(self, action_id, action) {$5},");
117
+ }
118
+ lines.push(" // Clean up when the component is deleted.");
119
+ lines.push(" final(self) {$6},");
120
+ lines.push(" // React to a hot reload of this script.");
121
+ lines.push(" on_reload(self) {$7},");
122
+ lines.push("});");
123
+ return lines;
124
+ }
125
+
126
+ function typedSnippetBody(factory: string, includeOnInput: boolean): string[] {
127
+ const lines = [
128
+ `import { ${factory} } from "@defold-typescript/types";`,
129
+ "",
130
+ "type Self = {",
131
+ " // Your script's state goes here.",
132
+ " $1",
133
+ "};",
134
+ "",
135
+ `export const script = ${factory}<Self>({`,
136
+ " // Initialize the component and return its state.",
137
+ " init(): Self {",
138
+ " return { $0 };",
139
+ " },",
140
+ " // Update the component every frame; `dt` is the time step.",
141
+ " update(self, dt) {$2},",
142
+ " // Update at the fixed physics time step.",
143
+ " fixed_update(self, dt) {$3},",
144
+ " // Update every frame after `update`.",
145
+ " late_update(self, dt) {$4},",
146
+ " // Handle an incoming message.",
147
+ " on_message(self, message_id, message, sender) {$5},",
148
+ ];
149
+ if (includeOnInput) {
150
+ lines.push(" // Handle input once input focus is acquired.");
151
+ lines.push(" on_input(self, action_id, action) {$6},");
152
+ }
153
+ lines.push(" // Clean up when the component is deleted.");
154
+ lines.push(" final(self) {$7},");
155
+ lines.push(" // React to a hot reload of this script.");
156
+ lines.push(" on_reload(self) {$8},");
157
+ lines.push("});");
158
+ return lines;
159
+ }
160
+
161
+ const VSCODE_SNIPPETS_CONTENT: Record<string, VscodeSnippet> = {
162
+ "Defold script (inferred self)": {
163
+ scope: "typescript",
164
+ prefix: "defold-script",
165
+ body: inlineSnippetBody("defineScript", true),
166
+ description: "Empty Defold script; state inferred from init's return.",
167
+ },
168
+ "Defold script (typed self)": {
169
+ scope: "typescript",
170
+ prefix: "defold-script-typed",
171
+ body: typedSnippetBody("defineScript", true),
172
+ description: "Empty Defold script with an explicit Self type.",
173
+ },
174
+ "Defold GUI script (inferred self)": {
175
+ scope: "typescript",
176
+ prefix: "defold-gui",
177
+ body: inlineSnippetBody("defineGuiScript", true),
178
+ description: "Empty Defold GUI script; state inferred from init's return.",
179
+ },
180
+ "Defold GUI script (typed self)": {
181
+ scope: "typescript",
182
+ prefix: "defold-gui-typed",
183
+ body: typedSnippetBody("defineGuiScript", true),
184
+ description: "Empty Defold GUI script with an explicit Self type.",
185
+ },
186
+ "Defold render script (inferred self)": {
187
+ scope: "typescript",
188
+ prefix: "defold-render",
189
+ body: inlineSnippetBody("defineRenderScript", false),
190
+ description: "Empty Defold render script; state inferred from init's return.",
191
+ },
192
+ "Defold render script (typed self)": {
193
+ scope: "typescript",
194
+ prefix: "defold-render-typed",
195
+ body: typedSnippetBody("defineRenderScript", false),
196
+ description: "Empty Defold render script with an explicit Self type.",
197
+ },
198
+ };
199
+
84
200
  const MAIN_TS_CONTENT = `export function init(): void {
85
201
  const start = vmath.vector3(0, 0, 0);
86
202
  msg.post("main:/hero", "spawn", { start });
@@ -301,6 +417,27 @@ function writeVscodeSettings(cwd: string, written: string[]): void {
301
417
  written.push(".vscode/settings.json");
302
418
  }
303
419
 
420
+ function writeVscodeSnippets(cwd: string, written: string[]): void {
421
+ const dir = path.join(cwd, ".vscode");
422
+ const filePath = path.join(dir, "defold-typescript.code-snippets");
423
+ if (existsSync(filePath)) {
424
+ const existing = readVscodeJson(filePath);
425
+ if (existing === null) {
426
+ return;
427
+ }
428
+ for (const [key, snippet] of Object.entries(VSCODE_SNIPPETS_CONTENT)) {
429
+ if (!(key in existing)) {
430
+ existing[key] = snippet;
431
+ }
432
+ }
433
+ writeJson(filePath, existing);
434
+ return;
435
+ }
436
+ mkdirSync(dir, { recursive: true });
437
+ writeJson(filePath, VSCODE_SNIPPETS_CONTENT);
438
+ written.push(".vscode/defold-typescript.code-snippets");
439
+ }
440
+
304
441
  function writeTsSurface(cwd: string, written: string[], force = false): ScriptKind | null {
305
442
  mkdirSync(path.join(cwd, "src"), { recursive: true });
306
443
  writeFileSync(path.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
@@ -349,6 +486,7 @@ function writeTsSurface(cwd: string, written: string[], force = false): ScriptKi
349
486
 
350
487
  writeVscodeExtensions(cwd, written);
351
488
  writeVscodeSettings(cwd, written);
489
+ writeVscodeSnippets(cwd, written);
352
490
 
353
491
  return selectScriptKind(kinds);
354
492
  }
@@ -70,6 +70,8 @@ export function materializeApiSurface(
70
70
  );
71
71
  const coreTypesSrc = path.join(srcDir, "core-types.ts");
72
72
  const includeCoreTypes = overloads.length > 0 && existsSync(coreTypesSrc);
73
+ const engineGlobalsSrc = path.join(srcDir, "engine-globals.d.ts");
74
+ const includeEngineGlobals = includeCoreTypes && existsSync(engineGlobalsSrc);
73
75
 
74
76
  const wanted = new Set(sources);
75
77
  for (const file of overloads) {
@@ -78,6 +80,9 @@ export function materializeApiSurface(
78
80
  if (includeCoreTypes) {
79
81
  wanted.add("core-types.d.ts");
80
82
  }
83
+ if (includeEngineGlobals) {
84
+ wanted.add("engine-globals.d.ts");
85
+ }
81
86
 
82
87
  for (const existing of readdirSync(absDir)) {
83
88
  if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
@@ -94,11 +99,17 @@ export function materializeApiSurface(
94
99
  if (includeCoreTypes) {
95
100
  writeFileSync(path.join(absDir, "core-types.d.ts"), readFileSync(coreTypesSrc, "utf8"));
96
101
  }
102
+ if (includeEngineGlobals) {
103
+ writeFileSync(path.join(absDir, "engine-globals.d.ts"), readFileSync(engineGlobalsSrc, "utf8"));
104
+ }
97
105
  for (const file of overloads) {
98
106
  writeFileSync(path.join(absDir, file), readFileSync(path.join(srcDir, file), "utf8"));
99
107
  }
100
108
 
101
109
  const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
110
+ if (includeEngineGlobals) {
111
+ modules.push("engine-globals");
112
+ }
102
113
  const imports = modules.map((mod) => `import "./${mod}";`).join("\n");
103
114
  writeFileSync(path.join(absDir, "index.d.ts"), `${imports}\n\nexport {};\n`);
104
115
 
@@ -216,6 +227,12 @@ export async function materializeRefDocSurface(
216
227
  ...(excludeModules.length > 0 ? { excludeModules } : {}),
217
228
  });
218
229
  copyFileSync(path.join(root, "src", "core-types.ts"), path.join(absDir, "core-types.d.ts"));
230
+ copyFileSync(
231
+ path.join(root, "src", "engine-globals.d.ts"),
232
+ path.join(absDir, "engine-globals.d.ts"),
233
+ );
234
+ const indexPath = path.join(absDir, "index.d.ts");
235
+ writeFileSync(indexPath, `import "./engine-globals";\n${readFileSync(indexPath, "utf8")}`);
219
236
  } catch {
220
237
  rmSync(absDir, { recursive: true, force: true });
221
238
  return { materializedDir: null, active: null };