-
Notifications
You must be signed in to change notification settings - Fork 190
/
Copy pathnode.ts
94 lines (85 loc) · 2.66 KB
/
node.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { assign } from '@glimmer/util';
import { SourceSpan } from '../../source/span';
export interface BaseNodeFields {
loc: SourceSpan;
}
/**
* This is a convenience function for creating ASTv2 nodes, with an optional name and the node's
* options.
*
* ```ts
* export class HtmlText extends node('HtmlText').fields<{ chars: string }>() {}
* ```
*
* This creates a new ASTv2 node with the name `'HtmlText'` and one field `chars: string` (in
* addition to a `loc: SourceOffsets` field, which all nodes have).
*
* ```ts
* export class Args extends node().fields<{
* positional: PositionalArguments;
* named: NamedArguments
* }>() {}
* ```
*
* This creates a new un-named ASTv2 node with two fields (`positional: Positional` and `named:
* Named`, in addition to the generic `loc: SourceOffsets` field).
*
* Once you create a node using `node`, it is instantiated with all of its fields (including `loc`):
*
* ```ts
* new HtmlText({ loc: offsets, chars: someString });
* ```
*/
export function node(): {
fields<Fields extends object>(): NodeConstructor<Fields & BaseNodeFields>;
};
export function node<T extends string>(
name: T
): {
fields<Fields extends object>(): TypedNodeConstructor<T, Fields & BaseNodeFields>;
};
export function node<T extends string>(
name?: T
):
| {
fields<Fields extends object>(): TypedNodeConstructor<T, Fields & BaseNodeFields>;
}
| {
fields<Fields extends object>(): NodeConstructor<Fields & BaseNodeFields>;
} {
if (name !== undefined) {
const type = name;
return {
fields<Fields extends object>(): TypedNodeConstructor<T, BaseNodeFields & Fields> {
return class {
// SAFETY: initialized via `assign` in the constructor.
declare readonly loc: SourceSpan;
readonly type: T;
constructor(fields: BaseNodeFields & Fields) {
this.type = type;
assign(this, fields);
}
} as TypedNodeConstructor<T, BaseNodeFields & Fields>;
},
};
} else {
return {
fields<Fields>(): NodeConstructor<Fields & BaseNodeFields> {
return class {
// SAFETY: initialized via `assign` in the constructor.
declare readonly loc: SourceSpan;
constructor(fields: BaseNodeFields & Fields) {
assign(this, fields);
}
} as NodeConstructor<BaseNodeFields & Fields>;
},
};
}
}
export interface NodeConstructor<Fields> {
new (fields: Fields): Readonly<Fields>;
}
type TypedNode<T extends string, Fields> = { type: T } & Readonly<Fields>;
export interface TypedNodeConstructor<T extends string, Fields> {
new (options: Fields): TypedNode<T, Fields>;
}