@defold-typescript/transpiler 0.46.0 → 0.47.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/hsm/index.ts CHANGED
@@ -865,6 +865,22 @@ interface Registered<Ctx, E extends EventObject> {
865
865
 
866
866
  const registered: { [key: string]: Registered<unknown, EventObject> } = {};
867
867
 
868
+ // Adds the fields the root's privateCtx returns to ctx. With onlyMissing, a field ctx already
869
+ // has keeps its value: a hot reload adds new private fields and leaves the old ones alone.
870
+ function fillPrivate(
871
+ target: { [name: string]: unknown },
872
+ root: unknown,
873
+ onlyMissing: boolean,
874
+ ): void {
875
+ const factory = (root as Partial<PrivateCtxConfig<unknown, object>>).privateCtx;
876
+ const fields = (factory?.() || {}) as { [name: string]: unknown };
877
+ for (const name in fields) {
878
+ if (!onlyMissing || target[name] === undefined) {
879
+ target[name] = fields[name];
880
+ }
881
+ }
882
+ }
883
+
868
884
  function createMachine<Ctx, E extends EventObject>(
869
885
  definition: Definition<Ctx, E>,
870
886
  ): Machine<Ctx, E> {
@@ -980,6 +996,7 @@ function createMachine<Ctx, E extends EventObject>(
980
996
  afterDelays = compiled.afterDelays;
981
997
  afterTargets = compiled.afterTargets;
982
998
  always = compiled.always;
999
+ fillPrivate(ctx as never, configs[ROOT], true);
983
1000
  growSlots();
984
1001
  const dropped: number[] = [];
985
1002
  for (let i = 0; i < order.length; i++) {
@@ -1553,6 +1570,7 @@ function createMachine<Ctx, E extends EventObject>(
1553
1570
  }
1554
1571
 
1555
1572
  busy = true;
1573
+ fillPrivate(ctx as never, configs[ROOT], false);
1556
1574
  enterState(ROOT);
1557
1575
  enterDefaults(ROOT, 0);
1558
1576
  refreshLeaves();
@@ -1599,3 +1617,114 @@ export function defineMachine<Ctx, E extends EventObject>(
1599
1617
  ): <T>(config: RootState<Ctx, E, T>) => Machine<Ctx, E, TreePath<T>> {
1600
1618
  return ((config: MachineConfig<Ctx, E>) => define(key, config)) as never;
1601
1619
  }
1620
+
1621
+ /**
1622
+ * The key a `definePrivateMachine` config adds to the root, beside `initial` and `states`.
1623
+ * @noSelf
1624
+ */
1625
+ export interface PrivateCtxConfig<Ctx, Priv> {
1626
+ /**
1627
+ * Builds the fields only the machine uses. It runs once per `start`, before the root's `enter`
1628
+ * hook, so every instance gets fresh fields of its own; a value written straight into the
1629
+ * config would be one array or table shared by every instance. It takes no arguments: to set
1630
+ * a private field from a field of `Ctx`, do it in the root's `enter` hook.
1631
+ *
1632
+ * The fields are added to the object passed to `start`, which stays the instance's `ctx`, so
1633
+ * restarting with `start(old.ctx)` keeps the public fields and builds the private ones fresh.
1634
+ * A hot reload under the machine's key adds the fields a new factory introduces and leaves
1635
+ * every field that already holds a value alone. A private field may not share a name with a
1636
+ * field of `Ctx`.
1637
+ *
1638
+ * The fields are hidden from callers by type only. At run time `ctx` is one table, and hooks,
1639
+ * `hsm-view` and a debugger see every field.
1640
+ */
1641
+ readonly privateCtx: () => Priv & { readonly [K in keyof Ctx & keyof Priv]: never };
1642
+ }
1643
+
1644
+ /**
1645
+ * Like `defineMachine`, for a machine that keeps fields of its own, such as a counter, a heat
1646
+ * level or a list it builds up. The config's `privateCtx` returns those fields. Every hook,
1647
+ * `when`, `run`, `task` and `update` sees them beside `Ctx`, while `start` takes only `Ctx` and
1648
+ * the instance it returns shows only `Ctx` in its `ctx`. A machine without fields of its own
1649
+ * uses `defineMachine`.
1650
+ *
1651
+ * There are two ways to type the fields.
1652
+ *
1653
+ * @example
1654
+ * **Inferred.** For a few fields, write them inline and let TypeScript infer their types. Add
1655
+ * `as` where the inferred type is wrong: an empty array is `never[]` until it reads
1656
+ * `[] as Hash[]`, and a field holding one of several strings is any `string` until it reads
1657
+ * `"cool" as "cool" | "hot"`.
1658
+ *
1659
+ * ```ts
1660
+ * type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
1661
+ *
1662
+ * export const turret = definePrivateMachine<{ readonly fireRate: number }, TurretEvent>("turret")({
1663
+ * privateCtx: () => ({ heat: 0, targets: [] as Hash[] }),
1664
+ * initial: "/idle",
1665
+ * states: {
1666
+ * idle: { on: { SPOTTED: "/firing" } },
1667
+ * firing: {
1668
+ * enter: (ctx) => {
1669
+ * ctx.heat += 1 / ctx.fireRate;
1670
+ * },
1671
+ * on: { LOST: "/idle" },
1672
+ * },
1673
+ * },
1674
+ * });
1675
+ *
1676
+ * const gun = turret.start({ fireRate: 2 }); // gun.ctx shows fireRate only
1677
+ * ```
1678
+ *
1679
+ * @example
1680
+ * **A named type.** For more fields, or when a helper outside the config needs the whole
1681
+ * context, declare the fields as an interface and make it the factory's return type. The fields
1682
+ * need no `as`, a misspelled or unknown field is a compile error, and the whole context is
1683
+ * `TurretOptions & TurretPrivate`. `satisfies TurretPrivate` does not do this: it checks the
1684
+ * object but keeps its narrow types, so an empty array stays `never[]`.
1685
+ *
1686
+ * ```ts
1687
+ * interface TurretOptions {
1688
+ * readonly fireRate: number;
1689
+ * }
1690
+ *
1691
+ * interface TurretPrivate {
1692
+ * heat: number;
1693
+ * mode: "cool" | "hot";
1694
+ * }
1695
+ *
1696
+ * type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
1697
+ *
1698
+ * const overheated = (ctx: TurretOptions & TurretPrivate) => ctx.heat > ctx.fireRate;
1699
+ *
1700
+ * export const turret = definePrivateMachine<TurretOptions, TurretEvent>("turret")({
1701
+ * privateCtx: (): TurretPrivate => ({ heat: 0, mode: "cool" }),
1702
+ * initial: "/idle",
1703
+ * states: {
1704
+ * idle: { on: { SPOTTED: "/firing" } },
1705
+ * firing: {
1706
+ * enter: (ctx) => {
1707
+ * ctx.heat += 1 / ctx.fireRate;
1708
+ * },
1709
+ * always: { to: "/cooling", when: overheated },
1710
+ * on: { LOST: "/idle" },
1711
+ * },
1712
+ * cooling: {
1713
+ * enter: (ctx) => {
1714
+ * ctx.mode = "hot";
1715
+ * },
1716
+ * exit: (ctx) => {
1717
+ * ctx.heat = 0;
1718
+ * ctx.mode = "cool";
1719
+ * },
1720
+ * after: { 2: "/idle" },
1721
+ * },
1722
+ * },
1723
+ * });
1724
+ * ```
1725
+ */
1726
+ export const definePrivateMachine: <Ctx, E extends EventObject>(
1727
+ key?: string,
1728
+ ) => <T, Priv extends object>(
1729
+ config: RootState<Ctx & Priv, E, T> & PrivateCtxConfig<Ctx, Priv>,
1730
+ ) => Machine<Ctx, E, TreePath<T>> = defineMachine as never;
package/dist/index.js CHANGED
@@ -3666,6 +3666,7 @@ function transpileVirtual(files, companionEmit, overrides = {}) {
3666
3666
  sourceMap: true,
3667
3667
  lib: ["lib.es2022.d.ts"],
3668
3668
  skipLibCheck: true,
3669
+ resolvePackageJsonImports: false,
3669
3670
  noImplicitSelf: true,
3670
3671
  luaPlugins: [
3671
3672
  { plugin: lifecycleErasurePlugin },
@@ -3745,6 +3746,7 @@ function compilerOptions(companionEmit) {
3745
3746
  sourceMap: true,
3746
3747
  lib: ["lib.es2022.d.ts"],
3747
3748
  skipLibCheck: true,
3749
+ resolvePackageJsonImports: false,
3748
3750
  noImplicitSelf: true,
3749
3751
  luaPlugins: [
3750
3752
  { plugin: lifecycleErasurePlugin },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defold-typescript/transpiler",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "description": "TypeScript-to-Lua build pipeline tuned for Defold's runtime.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,7 +35,7 @@
35
35
  "test": "bun test"
36
36
  },
37
37
  "dependencies": {
38
- "@defold-typescript/types": "0.46.0",
38
+ "@defold-typescript/types": "0.47.0",
39
39
  "@typescript-to-lua/language-extensions": "1.19.0",
40
40
  "typescript": "6.0.2",
41
41
  "typescript-to-lua": "^1.36.0"
package/src/session.ts CHANGED
@@ -35,6 +35,13 @@ function compilerOptions(companionEmit: CompanionEmitter): tstl.CompilerOptions
35
35
  // Don't cross-check the seeded ambient .d.ts surface against itself; only user
36
36
  // files matter (mirrors transpileProject and the editor's skipLibCheck).
37
37
  skipLibCheck: true,
38
+ // TypeScript turns this on under its default `bundler` resolution. With it on,
39
+ // an error that has to name a module-declared type the file does not import
40
+ // (`Url`, `Hash`, `Vector3`) throws inside the checker, which builds a module
41
+ // specifier from an undefined base path and the virtual host's empty current
42
+ // directory. The virtual host reads every `package.json` as empty, so no `#`
43
+ // import resolves with the option on or off.
44
+ resolvePackageJsonImports: false,
38
45
  // Defold scripts are not OO: free helper functions never receive a context,
39
46
  // so suppress TSTL's implicit `self` parameter and the `_G` call-site filler.
40
47
  noImplicitSelf: true,
package/src/transpile.ts CHANGED
@@ -341,6 +341,13 @@ function transpileVirtual(
341
341
  // Don't cross-check the seeded ambient .d.ts surface against itself; we only
342
342
  // care about diagnostics on user files (mirrors the editor's skipLibCheck).
343
343
  skipLibCheck: true,
344
+ // TypeScript turns this on under its default `bundler` resolution. With it on,
345
+ // an error that has to name a module-declared type the file does not import
346
+ // (`Url`, `Hash`, `Vector3`) throws inside the checker, which builds a module
347
+ // specifier from an undefined base path and the virtual host's empty current
348
+ // directory. The virtual host reads every `package.json` as empty, so no `#`
349
+ // import resolves with the option on or off.
350
+ resolvePackageJsonImports: false,
344
351
  // Defold scripts are not OO: free helper functions never receive a context,
345
352
  // so suppress TSTL's implicit `self` parameter and the `_G` call-site filler.
346
353
  noImplicitSelf: true,