@defold-typescript/transpiler 0.44.1 → 0.45.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 +151 -117
- package/package.json +2 -2
package/dist/hsm/index.ts
CHANGED
|
@@ -36,13 +36,27 @@ export type MoveAction<Ctx, E extends EventObject, V extends E = E> = (
|
|
|
36
36
|
machine: MachineInstance<Ctx, E>,
|
|
37
37
|
) => void;
|
|
38
38
|
|
|
39
|
+
// A list with a first entry: Lua cannot tell [] from {}, so an empty list would compile to a
|
|
40
|
+
// transition with no to. It is an object type, not a tuple, because TypeScript reports a bad
|
|
41
|
+
// `to` in the object form on that `to` only when the spec union holds no array type.
|
|
42
|
+
interface RuleList<Rule> {
|
|
43
|
+
readonly 0: Rule;
|
|
44
|
+
readonly [index: number]: Rule;
|
|
45
|
+
readonly length: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
/** @noSelf */
|
|
40
|
-
export interface TransitionConfig<
|
|
49
|
+
export interface TransitionConfig<
|
|
50
|
+
Ctx,
|
|
51
|
+
E extends EventObject,
|
|
52
|
+
V extends E = E,
|
|
53
|
+
To extends string = string,
|
|
54
|
+
> {
|
|
41
55
|
/**
|
|
42
56
|
* The full path of the state to move to. A `to` naming the state the rule is on exits and
|
|
43
57
|
* re-enters it, restarting its timers and `task`; a `to` below it keeps it active.
|
|
44
58
|
*/
|
|
45
|
-
readonly to?:
|
|
59
|
+
readonly to?: To;
|
|
46
60
|
/**
|
|
47
61
|
* The rule is taken only when this returns `true`. It must have no side effects: it is also
|
|
48
62
|
* checked when the move does not happen, in guarded lists and on every `always` recheck.
|
|
@@ -65,15 +79,16 @@ export interface TransitionConfig<Ctx, E extends EventObject, V extends E = E> {
|
|
|
65
79
|
readonly reenter?: never;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
export type TransitionSpec<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
export type TransitionSpec<
|
|
83
|
+
Ctx,
|
|
84
|
+
E extends EventObject,
|
|
85
|
+
V extends E = E,
|
|
86
|
+
To extends string = string,
|
|
87
|
+
> = To | TransitionConfig<Ctx, E, V, To> | RuleList<TransitionConfig<Ctx, E, V, To>>;
|
|
73
88
|
|
|
74
89
|
/** @noSelf */
|
|
75
|
-
export interface AlwaysConfig<Ctx> {
|
|
76
|
-
readonly to:
|
|
90
|
+
export interface AlwaysConfig<Ctx, To extends string = string> {
|
|
91
|
+
readonly to: To;
|
|
77
92
|
/** Checked on every recheck, so it must have no side effects. */
|
|
78
93
|
readonly when?: (ctx: Ctx) => boolean;
|
|
79
94
|
/** Renamed to `to`. */
|
|
@@ -82,13 +97,13 @@ export interface AlwaysConfig<Ctx> {
|
|
|
82
97
|
readonly guard?: never;
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
export type AlwaysSpec<Ctx> =
|
|
86
|
-
|
|
|
87
|
-
| AlwaysConfig<Ctx>
|
|
88
|
-
|
|
|
100
|
+
export type AlwaysSpec<Ctx, To extends string = string> =
|
|
101
|
+
| To
|
|
102
|
+
| AlwaysConfig<Ctx, To>
|
|
103
|
+
| RuleList<AlwaysConfig<Ctx, To>>;
|
|
89
104
|
|
|
90
|
-
export type OnConfig<Ctx, E extends EventObject> = {
|
|
91
|
-
readonly [K in E["type"]]?: TransitionSpec<Ctx, E, Extract<E, { type: K }
|
|
105
|
+
export type OnConfig<Ctx, E extends EventObject, To extends string = string> = {
|
|
106
|
+
readonly [K in E["type"]]?: TransitionSpec<Ctx, E, Extract<E, { type: K }>, To>;
|
|
92
107
|
};
|
|
93
108
|
|
|
94
109
|
/**
|
|
@@ -101,11 +116,11 @@ export type StateHook<Ctx, E extends EventObject> = (
|
|
|
101
116
|
machine: MachineInstance<Ctx, E>,
|
|
102
117
|
) => void;
|
|
103
118
|
|
|
104
|
-
export type UpdateHook<Ctx, E extends EventObject> = (
|
|
119
|
+
export type UpdateHook<Ctx, E extends EventObject, To extends string = string> = (
|
|
105
120
|
ctx: Ctx,
|
|
106
121
|
dt: number,
|
|
107
122
|
machine: MachineInstance<Ctx, E>,
|
|
108
|
-
) =>
|
|
123
|
+
) => To | undefined;
|
|
109
124
|
|
|
110
125
|
/**
|
|
111
126
|
* Starts work that lives as long as the state. `finish` sends one event and is ignored after the
|
|
@@ -119,29 +134,6 @@ export type TaskStart<Ctx, E extends EventObject> = (
|
|
|
119
134
|
// biome-ignore lint/suspicious/noConfusingVoidType: `void` keeps an expression-body task returning a void call (`=> go.animate(...)`) valid.
|
|
120
135
|
) => (() => void) | void;
|
|
121
136
|
|
|
122
|
-
/** @noSelf */
|
|
123
|
-
export interface StateConfig<Ctx, E extends EventObject> {
|
|
124
|
-
readonly type?: "parallel";
|
|
125
|
-
readonly initial?: string;
|
|
126
|
-
readonly restoreDepth?: number | "all";
|
|
127
|
-
readonly states?: { readonly [name: string]: StateConfig<Ctx, E> };
|
|
128
|
-
readonly on?: OnConfig<Ctx, E>;
|
|
129
|
-
readonly after?: { readonly [seconds: number]: string };
|
|
130
|
-
readonly always?: AlwaysSpec<Ctx>;
|
|
131
|
-
readonly enter?: StateHook<Ctx, E>;
|
|
132
|
-
readonly exit?: StateHook<Ctx, E>;
|
|
133
|
-
readonly update?: UpdateHook<Ctx, E>;
|
|
134
|
-
/** Started on each entry; see `TaskStart`. */
|
|
135
|
-
readonly task?: TaskStart<Ctx, E>;
|
|
136
|
-
/** Renamed to `task`. */
|
|
137
|
-
readonly invoke?: never;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface MachineConfig<Ctx, E extends EventObject> extends StateConfig<Ctx, E> {
|
|
141
|
-
readonly initial: string;
|
|
142
|
-
readonly states: { readonly [name: string]: StateConfig<Ctx, E> };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
137
|
/** @noSelf */
|
|
146
138
|
export interface Machine<Ctx, E extends EventObject, P extends string = string> {
|
|
147
139
|
readonly start: (ctx: Ctx) => MachineInstance<Ctx, E, P>;
|
|
@@ -149,92 +141,129 @@ export interface Machine<Ctx, E extends EventObject, P extends string = string>
|
|
|
149
141
|
|
|
150
142
|
type PathDepth = [never, 0, 1, 2, 3];
|
|
151
143
|
|
|
152
|
-
type
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
type NextCount = [never, 2, 3, 4, 5, 6];
|
|
145
|
+
|
|
146
|
+
// The paths a started machine can be in. A node with no children is `unknown`.
|
|
147
|
+
type TreePathsBelow<T, D extends number> = {
|
|
148
|
+
[K in keyof T & string]:
|
|
149
|
+
| K
|
|
150
|
+
| `${K}/${D extends 1
|
|
151
|
+
? keyof T[K] extends never
|
|
152
|
+
? never
|
|
153
|
+
: string
|
|
154
|
+
: TreePathsBelow<T[K], PathDepth[D]>}`;
|
|
155
|
+
}[keyof T & string];
|
|
156
|
+
|
|
157
|
+
type TreePath<T> = `/${TreePathsBelow<T, 4>}`;
|
|
158
|
+
|
|
159
|
+
// The paths a config slot accepts. TypeScript checks a config once before it types the
|
|
160
|
+
// callbacks, and a level whose states hold only callbacks has no inferred children in that
|
|
161
|
+
// check, so a node with no inferred children accepts any path below it.
|
|
162
|
+
type SlotPathsBelow<T, D extends number> = unknown extends T
|
|
163
|
+
? string
|
|
164
|
+
: {
|
|
165
|
+
[K in keyof T & string]:
|
|
155
166
|
| K
|
|
156
|
-
| `${K}/${D extends 1
|
|
157
|
-
|
|
158
|
-
? string
|
|
159
|
-
: never
|
|
160
|
-
: PathsBelow<Children[K], PathDepth[D]>}`;
|
|
161
|
-
}[keyof Children & string]
|
|
162
|
-
: never;
|
|
167
|
+
| `${K}/${D extends 1 ? string : SlotPathsBelow<T[K], PathDepth[D]>}`;
|
|
168
|
+
}[keyof T & string];
|
|
163
169
|
|
|
164
|
-
|
|
170
|
+
type SlotPath<T> = `/${SlotPathsBelow<T, 4>}`;
|
|
165
171
|
|
|
166
|
-
type
|
|
172
|
+
type TreeOf<C> = C extends { readonly states: infer Children }
|
|
173
|
+
? { readonly [K in keyof Children]: TreeOf<Children[K]> }
|
|
174
|
+
: unknown;
|
|
167
175
|
|
|
168
|
-
type
|
|
169
|
-
[K in keyof Children]: RestoreCount<Children[K], D>;
|
|
170
|
-
}[keyof Children];
|
|
176
|
+
export type StatePath<C> = TreePath<TreeOf<C>>;
|
|
171
177
|
|
|
172
|
-
// The
|
|
173
|
-
//
|
|
174
|
-
type
|
|
175
|
-
?
|
|
178
|
+
// The levels of child states below a node. It counts tree levels, so above a parallel state
|
|
179
|
+
// it is an upper bound on the counts restoreDepth takes.
|
|
180
|
+
type TreeDepth<T, D extends number> = unknown extends T
|
|
181
|
+
? number
|
|
182
|
+
: [D] extends [never]
|
|
176
183
|
? number
|
|
177
|
-
:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
184
|
+
:
|
|
185
|
+
| 1
|
|
186
|
+
| ({
|
|
187
|
+
[K in keyof T]: unknown extends T[K] ? never : TreeDepth<T[K], PathDepth[D]>;
|
|
188
|
+
}[keyof T] extends infer N
|
|
189
|
+
? N extends number
|
|
190
|
+
? number extends N
|
|
191
|
+
? number
|
|
192
|
+
: NextCount[N]
|
|
193
|
+
: never
|
|
194
|
+
: never);
|
|
195
|
+
|
|
196
|
+
type AnyChild<Ctx, E extends EventObject, T, To extends string> = unknown extends T
|
|
197
|
+
? { readonly [name: string]: StateNode<Ctx, E, unknown, string, To> }
|
|
198
|
+
: unknown;
|
|
199
|
+
|
|
200
|
+
// `states` is the only slot that infers the tree; every other slot that mentions it is wrapped
|
|
201
|
+
// in NoInfer. `on`, `after` and `update` are wrapped whole so inference never reads them:
|
|
202
|
+
// without exactOptionalPropertyTypes, reading `on` asks for the paths while the tree is still
|
|
203
|
+
// being inferred, and a valid config fails as circular. `always` is wrapped only through `To`:
|
|
204
|
+
// NoInfer over a union that holds object types hides its string members from completion.
|
|
205
|
+
// No callback parameter mentions the tree: one that did would make TypeScript settle the tree
|
|
206
|
+
// at the first such callback, before the states beside it are read.
|
|
207
|
+
/** @noSelf */
|
|
208
|
+
interface StateBase<Ctx, E extends EventObject, T, Self extends string, To extends string> {
|
|
209
|
+
readonly states?: {
|
|
210
|
+
readonly [K in keyof T]: StateNode<Ctx, E, T[K], `${Self}/${K & string}`, To>;
|
|
211
|
+
} & NoInfer<AnyChild<Ctx, E, T, To>>;
|
|
212
|
+
readonly on?: NoInfer<OnConfig<Ctx, E, To>>;
|
|
213
|
+
readonly after?: NoInfer<{ readonly [seconds: number]: To }>;
|
|
214
|
+
readonly always?: AlwaysSpec<Ctx, To>;
|
|
215
|
+
readonly enter?: StateHook<Ctx, E>;
|
|
216
|
+
readonly exit?: StateHook<Ctx, E>;
|
|
217
|
+
readonly update?: NoInfer<UpdateHook<Ctx, E, To>>;
|
|
218
|
+
/** Started on each entry; see `TaskStart`. */
|
|
219
|
+
readonly task?: TaskStart<Ctx, E>;
|
|
220
|
+
/** Renamed to `task`. */
|
|
221
|
+
readonly invoke?: never;
|
|
194
222
|
}
|
|
195
223
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
readonly
|
|
202
|
-
|
|
203
|
-
? never
|
|
204
|
-
: S extends { readonly states: infer Children }
|
|
205
|
-
? `${Self}/${keyof Children & string}`
|
|
206
|
-
: never;
|
|
207
|
-
readonly restoreDepth?: S extends { readonly type: "parallel" }
|
|
208
|
-
? never
|
|
209
|
-
: S extends { readonly states: object }
|
|
210
|
-
? RestoreCount<S, 4> | "all"
|
|
211
|
-
: never;
|
|
212
|
-
readonly states?: S extends { readonly states: infer Children }
|
|
213
|
-
? {
|
|
214
|
-
readonly [K in keyof Children]: PathCheck<Children[K], `${Self}/${K & string}`, All, Ev>;
|
|
215
|
-
}
|
|
216
|
-
: unknown;
|
|
217
|
-
readonly on?: S extends { readonly on: infer On }
|
|
218
|
-
? { readonly [K in keyof On]: K extends Ev ? SpecCheck<All> : never }
|
|
219
|
-
: unknown;
|
|
220
|
-
readonly after?: S extends { readonly after: infer After }
|
|
221
|
-
? { readonly [K in keyof After]: All }
|
|
222
|
-
: unknown;
|
|
223
|
-
readonly always?: S extends { readonly always: unknown } ? SpecCheck<All> : unknown;
|
|
224
|
-
readonly enter?: unknown;
|
|
225
|
-
readonly exit?: unknown;
|
|
226
|
-
readonly update?: unknown;
|
|
227
|
-
readonly task?: unknown;
|
|
228
|
-
};
|
|
224
|
+
/** @noSelf */
|
|
225
|
+
interface CompoundState<Ctx, E extends EventObject, T, Self extends string, To extends string>
|
|
226
|
+
extends StateBase<Ctx, E, T, Self, To> {
|
|
227
|
+
readonly type?: never;
|
|
228
|
+
readonly initial?: NoInfer<unknown extends T ? string : `${Self}/${keyof T & string}`>;
|
|
229
|
+
readonly restoreDepth?: NoInfer<TreeDepth<T, 4> | "all">;
|
|
230
|
+
}
|
|
229
231
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
/** @noSelf */
|
|
233
|
+
interface ParallelState<Ctx, E extends EventObject, T, Self extends string, To extends string>
|
|
234
|
+
extends StateBase<Ctx, E, T, Self, To> {
|
|
235
|
+
readonly type: "parallel";
|
|
236
|
+
readonly initial?: never;
|
|
237
|
+
readonly restoreDepth?: never;
|
|
232
238
|
}
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
type StateNode<Ctx, E extends EventObject, T, Self extends string, To extends string> =
|
|
241
|
+
| CompoundState<Ctx, E, T, Self, To>
|
|
242
|
+
| ParallelState<Ctx, E, T, Self, To>;
|
|
243
|
+
|
|
244
|
+
type RootState<Ctx, E extends EventObject, T> = CompoundState<
|
|
245
|
+
Ctx,
|
|
246
|
+
E,
|
|
247
|
+
T,
|
|
248
|
+
"",
|
|
249
|
+
NoInfer<SlotPath<T>>
|
|
250
|
+
> & {
|
|
251
|
+
readonly initial: string;
|
|
252
|
+
readonly states: object;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export type StateConfig<Ctx, E extends EventObject> = StateNode<Ctx, E, unknown, string, string>;
|
|
256
|
+
|
|
257
|
+
export type MachineConfig<Ctx, E extends EventObject> = CompoundState<
|
|
258
|
+
Ctx,
|
|
259
|
+
E,
|
|
260
|
+
unknown,
|
|
261
|
+
string,
|
|
262
|
+
string
|
|
263
|
+
> & {
|
|
264
|
+
readonly initial: string;
|
|
265
|
+
readonly states: { readonly [name: string]: StateConfig<Ctx, E> };
|
|
266
|
+
};
|
|
238
267
|
|
|
239
268
|
type Guard<Ctx, E extends EventObject> = (ctx: Ctx, event: E) => boolean;
|
|
240
269
|
|
|
@@ -550,7 +579,12 @@ function compileAlways<Ctx, E extends EventObject>(
|
|
|
550
579
|
): void {
|
|
551
580
|
const list: Transition<Ctx, E>[] = [];
|
|
552
581
|
compiled.always[index] = list;
|
|
553
|
-
|
|
582
|
+
// Read as an array type: the transpiler shifts an index to Lua's base 1 only on an array.
|
|
583
|
+
const spec = (compiled.configs[index] as StateConfig<Ctx, E>).always as
|
|
584
|
+
| string
|
|
585
|
+
| AlwaysConfig<Ctx>
|
|
586
|
+
| readonly AlwaysConfig<Ctx>[]
|
|
587
|
+
| undefined;
|
|
554
588
|
if (spec === undefined) {
|
|
555
589
|
return;
|
|
556
590
|
}
|
|
@@ -1413,6 +1447,6 @@ function define<Ctx, E extends EventObject>(
|
|
|
1413
1447
|
// A key makes a later definition under it (a hot-reloaded module re-running) rebind live instances.
|
|
1414
1448
|
export function defineMachine<Ctx, E extends EventObject>(
|
|
1415
1449
|
key?: string,
|
|
1416
|
-
): <
|
|
1450
|
+
): <T>(config: RootState<Ctx, E, T>) => Machine<Ctx, E, TreePath<T>> {
|
|
1417
1451
|
return ((config: MachineConfig<Ctx, E>) => define(key, config)) as never;
|
|
1418
1452
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defold-typescript/transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.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.
|
|
38
|
+
"@defold-typescript/types": "0.45.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"
|