@flowgram-vue/variable-core 0.2.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/LICENSE +22 -0
- package/dist/index.cjs +2630 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2293 -0
- package/dist/index.js +2592 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
- package/src/ast/ast-node.ts +374 -0
- package/src/ast/ast-registers.ts +129 -0
- package/src/ast/common/data-node.ts +63 -0
- package/src/ast/common/index.ts +8 -0
- package/src/ast/common/list-node.ts +70 -0
- package/src/ast/common/map-node.ts +89 -0
- package/src/ast/declaration/base-variable-field.ts +184 -0
- package/src/ast/declaration/index.ts +13 -0
- package/src/ast/declaration/property.ts +19 -0
- package/src/ast/declaration/variable-declaration-list.ts +112 -0
- package/src/ast/declaration/variable-declaration.ts +78 -0
- package/src/ast/expression/base-expression.ts +117 -0
- package/src/ast/expression/enumerate-expression.ts +77 -0
- package/src/ast/expression/index.ts +10 -0
- package/src/ast/expression/keypath-expression.ts +157 -0
- package/src/ast/expression/legacy-keypath-expression.ts +119 -0
- package/src/ast/expression/wrap-array-expression.ts +96 -0
- package/src/ast/factory.ts +163 -0
- package/src/ast/flags.ts +50 -0
- package/src/ast/index.ts +26 -0
- package/src/ast/match.ts +146 -0
- package/src/ast/type/array.ts +109 -0
- package/src/ast/type/base-type.ts +49 -0
- package/src/ast/type/boolean.ts +26 -0
- package/src/ast/type/custom-type.ts +70 -0
- package/src/ast/type/index.ts +19 -0
- package/src/ast/type/integer.ts +29 -0
- package/src/ast/type/map.ts +96 -0
- package/src/ast/type/number.ts +26 -0
- package/src/ast/type/object.ts +185 -0
- package/src/ast/type/string.ts +55 -0
- package/src/ast/type/union.ts +13 -0
- package/src/ast/types.ts +188 -0
- package/src/ast/utils/expression.ts +61 -0
- package/src/ast/utils/helpers.ts +73 -0
- package/src/ast/utils/inversify.ts +42 -0
- package/src/ast/utils/observable.ts +5 -0
- package/src/ast/utils/variable-field.ts +25 -0
- package/src/composables/index.ts +9 -0
- package/src/composables/scope-provider.ts +78 -0
- package/src/composables/use-available-variables.ts +39 -0
- package/src/composables/use-output-variables.ts +36 -0
- package/src/composables/use-scope-available.ts +32 -0
- package/src/index.ts +14 -0
- package/src/providers.ts +22 -0
- package/src/scope/datas/index.ts +8 -0
- package/src/scope/datas/scope-available-data.ts +234 -0
- package/src/scope/datas/scope-event-data.ts +67 -0
- package/src/scope/datas/scope-output-data.ts +151 -0
- package/src/scope/index.ts +9 -0
- package/src/scope/scope-chain.ts +69 -0
- package/src/scope/scope.ts +200 -0
- package/src/scope/types.ts +102 -0
- package/src/scope/variable-table.ts +203 -0
- package/src/services/index.ts +6 -0
- package/src/services/variable-field-key-rename-service.ts +131 -0
- package/src/utils/memo.ts +38 -0
- package/src/utils/toDisposable.ts +16 -0
- package/src/variable-container-module.ts +28 -0
- package/src/variable-engine.ts +197 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { xor } from 'lodash-es';
|
|
7
|
+
|
|
8
|
+
import { parseTypeJsonOrKind } from '../utils/helpers';
|
|
9
|
+
import { ASTNodeJSON, ASTKind, ASTNodeJSONOrKind, type GlobalEventActionType } from '../types';
|
|
10
|
+
import { ASTNodeFlags } from '../flags';
|
|
11
|
+
import { Property, type PropertyJSON } from '../declaration/property';
|
|
12
|
+
import { BaseType } from './base-type';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ASTNodeJSON representation of `ObjectType`
|
|
16
|
+
*/
|
|
17
|
+
export interface ObjectJSON<VariableMeta = any> {
|
|
18
|
+
/**
|
|
19
|
+
* The properties of the object.
|
|
20
|
+
*
|
|
21
|
+
* The `properties` of an Object must be of type `Property`, so the business can omit the `kind` field.
|
|
22
|
+
*/
|
|
23
|
+
properties?: PropertyJSON<VariableMeta>[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Action type for object properties change.
|
|
28
|
+
*/
|
|
29
|
+
export type ObjectPropertiesChangeAction = GlobalEventActionType<
|
|
30
|
+
'ObjectPropertiesChange',
|
|
31
|
+
{
|
|
32
|
+
prev: Property[];
|
|
33
|
+
next: Property[];
|
|
34
|
+
},
|
|
35
|
+
ObjectType
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Represents an object type.
|
|
40
|
+
*/
|
|
41
|
+
export class ObjectType extends BaseType<ObjectJSON> {
|
|
42
|
+
public flags: ASTNodeFlags = ASTNodeFlags.DrilldownType;
|
|
43
|
+
|
|
44
|
+
static kind: string = ASTKind.Object;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A map of property keys to `Property` instances.
|
|
48
|
+
*/
|
|
49
|
+
propertyTable: Map<string, Property> = new Map();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An array of `Property` instances.
|
|
53
|
+
*/
|
|
54
|
+
properties: Property[];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Deserializes the `ObjectJSON` to the `ObjectType`.
|
|
58
|
+
* @param json The `ObjectJSON` to deserialize.
|
|
59
|
+
*/
|
|
60
|
+
fromJSON({ properties }: ObjectJSON): void {
|
|
61
|
+
const removedKeys = new Set(this.propertyTable.keys());
|
|
62
|
+
const prev = [...(this.properties || [])];
|
|
63
|
+
|
|
64
|
+
// Iterate over the new properties.
|
|
65
|
+
this.properties = (properties || []).map((property: PropertyJSON) => {
|
|
66
|
+
const existProperty = this.propertyTable.get(property.key);
|
|
67
|
+
removedKeys.delete(property.key);
|
|
68
|
+
|
|
69
|
+
if (existProperty) {
|
|
70
|
+
existProperty.fromJSON(property as PropertyJSON);
|
|
71
|
+
|
|
72
|
+
return existProperty;
|
|
73
|
+
} else {
|
|
74
|
+
const newProperty = this.createChildNode({
|
|
75
|
+
...property,
|
|
76
|
+
kind: ASTKind.Property,
|
|
77
|
+
}) as Property;
|
|
78
|
+
|
|
79
|
+
this.fireChange();
|
|
80
|
+
|
|
81
|
+
this.propertyTable.set(property.key, newProperty);
|
|
82
|
+
// TODO: When a child node is actively destroyed, delete the information in the table.
|
|
83
|
+
|
|
84
|
+
return newProperty;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Delete properties that no longer exist.
|
|
89
|
+
removedKeys.forEach((key) => {
|
|
90
|
+
const property = this.propertyTable.get(key);
|
|
91
|
+
property?.dispose();
|
|
92
|
+
this.propertyTable.delete(key);
|
|
93
|
+
this.fireChange();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
this.dispatchGlobalEvent<ObjectPropertiesChangeAction>({
|
|
97
|
+
type: 'ObjectPropertiesChange',
|
|
98
|
+
payload: {
|
|
99
|
+
prev,
|
|
100
|
+
next: [...this.properties],
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Serialize the `ObjectType` to `ObjectJSON`.
|
|
107
|
+
* @returns The JSON representation of `ObjectType`.
|
|
108
|
+
*/
|
|
109
|
+
toJSON() {
|
|
110
|
+
return {
|
|
111
|
+
properties: this.properties.map((_property) => _property.toJSON()),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get a variable field by key path.
|
|
117
|
+
* @param keyPath The key path to search for.
|
|
118
|
+
* @returns The variable field if found, otherwise `undefined`.
|
|
119
|
+
*/
|
|
120
|
+
getByKeyPath(keyPath: string[]): Property | undefined {
|
|
121
|
+
const [curr, ...restKeyPath] = keyPath;
|
|
122
|
+
|
|
123
|
+
const property = this.propertyTable.get(curr);
|
|
124
|
+
|
|
125
|
+
// Found the end of the path.
|
|
126
|
+
if (!restKeyPath.length) {
|
|
127
|
+
return property;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Otherwise, continue searching downwards.
|
|
131
|
+
if (property?.type && property?.type?.flags & ASTNodeFlags.DrilldownType) {
|
|
132
|
+
return property.type.getByKeyPath(restKeyPath) as Property | undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if the current type is equal to the target type.
|
|
140
|
+
* @param targetTypeJSONOrKind The type to compare with.
|
|
141
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
142
|
+
*/
|
|
143
|
+
public isTypeEqual(targetTypeJSONOrKind?: ASTNodeJSONOrKind): boolean {
|
|
144
|
+
const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
|
|
145
|
+
const isSuperEqual = super.isTypeEqual(targetTypeJSONOrKind);
|
|
146
|
+
|
|
147
|
+
if (targetTypeJSON?.weak || targetTypeJSON?.kind === ASTKind.Union) {
|
|
148
|
+
return isSuperEqual;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
targetTypeJSON &&
|
|
153
|
+
isSuperEqual &&
|
|
154
|
+
// Weak comparison, only need to compare the Kind.
|
|
155
|
+
(targetTypeJSON?.weak || this.customStrongEqual(targetTypeJSON))
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Object type strong comparison.
|
|
161
|
+
* @param targetTypeJSON The type to compare with.
|
|
162
|
+
* @returns `true` if the types are equal, `false` otherwise.
|
|
163
|
+
*/
|
|
164
|
+
protected customStrongEqual(targetTypeJSON: ASTNodeJSON): boolean {
|
|
165
|
+
const targetProperties = (targetTypeJSON as ObjectJSON).properties || [];
|
|
166
|
+
|
|
167
|
+
const sourcePropertyKeys = Array.from(this.propertyTable.keys());
|
|
168
|
+
const targetPropertyKeys = targetProperties.map((_target) => _target.key);
|
|
169
|
+
|
|
170
|
+
const isKeyStrongEqual = !xor(sourcePropertyKeys, targetPropertyKeys).length;
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
isKeyStrongEqual &&
|
|
174
|
+
targetProperties.every((targetProperty) => {
|
|
175
|
+
const sourceProperty = this.propertyTable.get(targetProperty.key);
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
sourceProperty &&
|
|
179
|
+
sourceProperty.key === targetProperty.key &&
|
|
180
|
+
sourceProperty.type?.isTypeEqual(targetProperty?.type)
|
|
181
|
+
);
|
|
182
|
+
})
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ASTKind } from '../types';
|
|
7
|
+
import { ASTNodeFlags } from '../flags';
|
|
8
|
+
import { BaseType } from './base-type';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ASTNodeJSON representation of the `StringType`.
|
|
12
|
+
*/
|
|
13
|
+
export interface StringJSON {
|
|
14
|
+
/**
|
|
15
|
+
* see https://json-schema.org/understanding-json-schema/reference/type#format
|
|
16
|
+
*/
|
|
17
|
+
format?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class StringType extends BaseType<StringJSON> {
|
|
21
|
+
public flags: ASTNodeFlags = ASTNodeFlags.BasicType;
|
|
22
|
+
|
|
23
|
+
static kind: string = ASTKind.String;
|
|
24
|
+
|
|
25
|
+
protected _format?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* see https://json-schema.org/understanding-json-schema/reference/string#format
|
|
29
|
+
*/
|
|
30
|
+
get format() {
|
|
31
|
+
return this._format;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Deserialize the `StringJSON` to the `StringType`.
|
|
36
|
+
*
|
|
37
|
+
* @param json StringJSON representation of the `StringType`.
|
|
38
|
+
*/
|
|
39
|
+
fromJSON(json?: StringJSON): void {
|
|
40
|
+
if (json?.format !== this._format) {
|
|
41
|
+
this._format = json?.format;
|
|
42
|
+
this.fireChange();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Serialize the `StringType` to `StringJSON`.
|
|
48
|
+
* @returns The JSON representation of `StringType`.
|
|
49
|
+
*/
|
|
50
|
+
toJSON() {
|
|
51
|
+
return {
|
|
52
|
+
format: this._format,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ASTNodeJSONOrKind } from '../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ASTNodeJSON representation of `UnionType`, which union multiple `BaseType`.
|
|
10
|
+
*/
|
|
11
|
+
export interface UnionJSON {
|
|
12
|
+
types?: ASTNodeJSONOrKind[];
|
|
13
|
+
}
|
package/src/ast/types.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Observer } from 'rxjs';
|
|
7
|
+
|
|
8
|
+
import { type Scope } from '../scope';
|
|
9
|
+
import { type ASTNode } from './ast-node';
|
|
10
|
+
|
|
11
|
+
export type ASTKindType = string;
|
|
12
|
+
export type Identifier = string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ASTNodeJSON is the JSON representation of an ASTNode.
|
|
16
|
+
*/
|
|
17
|
+
export interface ASTNodeJSON {
|
|
18
|
+
/**
|
|
19
|
+
* Kind is the type of the AST node.
|
|
20
|
+
*/
|
|
21
|
+
kind?: ASTKindType;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Key is the unique identifier of the node.
|
|
25
|
+
* If not provided, the node will generate a default key value.
|
|
26
|
+
*/
|
|
27
|
+
key?: Identifier;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Core AST node types.
|
|
33
|
+
*/
|
|
34
|
+
export enum ASTKind {
|
|
35
|
+
/**
|
|
36
|
+
* # Type-related.
|
|
37
|
+
* - A set of type AST nodes based on JSON types is implemented internally by default.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* String type.
|
|
42
|
+
*/
|
|
43
|
+
String = 'String',
|
|
44
|
+
/**
|
|
45
|
+
* Number type.
|
|
46
|
+
*/
|
|
47
|
+
Number = 'Number',
|
|
48
|
+
/**
|
|
49
|
+
* Integer type.
|
|
50
|
+
*/
|
|
51
|
+
Integer = 'Integer',
|
|
52
|
+
/**
|
|
53
|
+
* Boolean type.
|
|
54
|
+
*/
|
|
55
|
+
Boolean = 'Boolean',
|
|
56
|
+
/**
|
|
57
|
+
* Object type.
|
|
58
|
+
*/
|
|
59
|
+
Object = 'Object',
|
|
60
|
+
/**
|
|
61
|
+
* Array type.
|
|
62
|
+
*/
|
|
63
|
+
Array = 'Array',
|
|
64
|
+
/**
|
|
65
|
+
* Map type.
|
|
66
|
+
*/
|
|
67
|
+
Map = 'Map',
|
|
68
|
+
/**
|
|
69
|
+
* Union type.
|
|
70
|
+
* Commonly used for type checking, generally not exposed to the business.
|
|
71
|
+
*/
|
|
72
|
+
Union = 'Union',
|
|
73
|
+
/**
|
|
74
|
+
* Any type.
|
|
75
|
+
* Commonly used for business logic.
|
|
76
|
+
*/
|
|
77
|
+
Any = 'Any',
|
|
78
|
+
/**
|
|
79
|
+
* Custom type.
|
|
80
|
+
* For business-defined types.
|
|
81
|
+
*/
|
|
82
|
+
CustomType = 'CustomType',
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* # Declaration-related.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Field definition for Object drill-down.
|
|
90
|
+
*/
|
|
91
|
+
Property = 'Property',
|
|
92
|
+
/**
|
|
93
|
+
* Variable declaration.
|
|
94
|
+
*/
|
|
95
|
+
VariableDeclaration = 'VariableDeclaration',
|
|
96
|
+
/**
|
|
97
|
+
* Variable declaration list.
|
|
98
|
+
*/
|
|
99
|
+
VariableDeclarationList = 'VariableDeclarationList',
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* # Expression-related.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Access fields on variables through the path system.
|
|
107
|
+
*/
|
|
108
|
+
KeyPathExpression = 'KeyPathExpression',
|
|
109
|
+
/**
|
|
110
|
+
* Iterate over specified data.
|
|
111
|
+
*/
|
|
112
|
+
EnumerateExpression = 'EnumerateExpression',
|
|
113
|
+
/**
|
|
114
|
+
* Wrap with Array Type.
|
|
115
|
+
*/
|
|
116
|
+
WrapArrayExpression = 'WrapArrayExpression',
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* # General-purpose AST nodes.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* General-purpose List<ASTNode> storage node.
|
|
124
|
+
*/
|
|
125
|
+
ListNode = 'ListNode',
|
|
126
|
+
/**
|
|
127
|
+
* General-purpose data storage node.
|
|
128
|
+
*/
|
|
129
|
+
DataNode = 'DataNode',
|
|
130
|
+
/**
|
|
131
|
+
* General-purpose Map<string, ASTNode> storage node.
|
|
132
|
+
*/
|
|
133
|
+
MapNode = 'MapNode',
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface CreateASTParams {
|
|
137
|
+
scope: Scope;
|
|
138
|
+
key?: Identifier;
|
|
139
|
+
parent?: ASTNode;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type ASTNodeJSONOrKind = string | ASTNodeJSON;
|
|
143
|
+
|
|
144
|
+
export type ObserverOrNext<T> = Partial<Observer<T>> | ((value: T) => void);
|
|
145
|
+
|
|
146
|
+
export interface SubscribeConfig<This, Data> {
|
|
147
|
+
// Merge all changes within one animationFrame into a single one.
|
|
148
|
+
debounceAnimation?: boolean;
|
|
149
|
+
// Respond with a value by default upon subscription.
|
|
150
|
+
triggerOnInit?: boolean;
|
|
151
|
+
selector?: (curr: This) => Data;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* TypeUtils to get the JSON representation of an AST node with a specific kind.
|
|
156
|
+
*/
|
|
157
|
+
export type GetKindJSON<KindType extends string, JSON extends ASTNodeJSON> = {
|
|
158
|
+
kind: KindType;
|
|
159
|
+
key?: Identifier;
|
|
160
|
+
} & JSON;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* TypeUtils to get the JSON representation of an AST node with a specific kind or just the kind string.
|
|
164
|
+
*/
|
|
165
|
+
export type GetKindJSONOrKind<KindType extends string, JSON extends ASTNodeJSON> =
|
|
166
|
+
| ({
|
|
167
|
+
kind: KindType;
|
|
168
|
+
key?: Identifier;
|
|
169
|
+
} & JSON)
|
|
170
|
+
| KindType;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Global event action type.
|
|
174
|
+
* - Global event might be dispatched from `ASTNode` or `Scope`.
|
|
175
|
+
*/
|
|
176
|
+
export interface GlobalEventActionType<
|
|
177
|
+
Type = string,
|
|
178
|
+
Payload = any,
|
|
179
|
+
AST extends ASTNode = ASTNode
|
|
180
|
+
> {
|
|
181
|
+
type: Type;
|
|
182
|
+
payload?: Payload;
|
|
183
|
+
ast?: AST;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type NewASTAction = GlobalEventActionType<'NewAST'>;
|
|
187
|
+
export type UpdateASTAction = GlobalEventActionType<'UpdateAST'>;
|
|
188
|
+
export type DisposeASTAction = GlobalEventActionType<'DisposeAST'>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { intersection } from 'lodash-es';
|
|
7
|
+
|
|
8
|
+
import { ASTNodeFlags } from '../flags';
|
|
9
|
+
import { type BaseExpression } from '../expression';
|
|
10
|
+
import { type BaseVariableField } from '../declaration';
|
|
11
|
+
import { type ASTNode } from '../ast-node';
|
|
12
|
+
import { getParentFields } from './variable-field';
|
|
13
|
+
import { getAllChildren } from './helpers';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get all variables referenced by child ASTs.
|
|
17
|
+
* @param ast The ASTNode to traverse.
|
|
18
|
+
* @returns All variables referenced by child ASTs.
|
|
19
|
+
*/
|
|
20
|
+
export function getAllRefs(ast: ASTNode): BaseVariableField[] {
|
|
21
|
+
return getAllChildren(ast)
|
|
22
|
+
.filter((_child) => _child.flags & ASTNodeFlags.Expression)
|
|
23
|
+
.map((_child) => (_child as BaseExpression).refs)
|
|
24
|
+
.flat()
|
|
25
|
+
.filter(Boolean) as BaseVariableField[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Checks for circular references.
|
|
30
|
+
* @param curr The current expression.
|
|
31
|
+
* @param refNode The referenced variable node.
|
|
32
|
+
* @returns Whether a circular reference exists.
|
|
33
|
+
*/
|
|
34
|
+
export function checkRefCycle(
|
|
35
|
+
curr: BaseExpression,
|
|
36
|
+
refNodes: (BaseVariableField | undefined)[]
|
|
37
|
+
): boolean {
|
|
38
|
+
// If there are no circular references in the scope, then it is impossible to have a circular reference.
|
|
39
|
+
if (
|
|
40
|
+
intersection(curr.scope.coverScopes, refNodes.map((_ref) => _ref?.scope).filter(Boolean))
|
|
41
|
+
.length === 0
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// BFS traversal.
|
|
47
|
+
const visited = new Set<BaseVariableField>();
|
|
48
|
+
const queue = [...refNodes];
|
|
49
|
+
|
|
50
|
+
while (queue.length) {
|
|
51
|
+
const currNode = queue.shift()!;
|
|
52
|
+
visited.add(currNode);
|
|
53
|
+
|
|
54
|
+
for (const ref of getAllRefs(currNode).filter((_ref) => !visited.has(_ref))) {
|
|
55
|
+
queue.push(ref);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If the referenced variables include the parent variable of the expression, then there is a circular reference.
|
|
60
|
+
return intersection(Array.from(visited), getParentFields(curr)).length > 0;
|
|
61
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
|
7
|
+
import { ASTMatch } from '../match';
|
|
8
|
+
import { ASTNode } from '../ast-node';
|
|
9
|
+
|
|
10
|
+
export function updateChildNodeHelper(
|
|
11
|
+
this: ASTNode,
|
|
12
|
+
{
|
|
13
|
+
getChildNode,
|
|
14
|
+
updateChildNode,
|
|
15
|
+
removeChildNode,
|
|
16
|
+
nextJSON,
|
|
17
|
+
}: {
|
|
18
|
+
getChildNode: () => ASTNode | undefined;
|
|
19
|
+
updateChildNode: (nextNode: ASTNode) => void;
|
|
20
|
+
removeChildNode: () => void;
|
|
21
|
+
nextJSON?: ASTNodeJSON;
|
|
22
|
+
}
|
|
23
|
+
): ASTNode | undefined {
|
|
24
|
+
const currNode: ASTNode | undefined = getChildNode();
|
|
25
|
+
|
|
26
|
+
const isNewKind = currNode?.kind !== nextJSON?.kind;
|
|
27
|
+
// If `nextJSON` does not pass a key value, the key value remains unchanged by default.
|
|
28
|
+
const isNewKey = nextJSON?.key && nextJSON?.key !== currNode?.key;
|
|
29
|
+
|
|
30
|
+
if (isNewKind || isNewKey) {
|
|
31
|
+
// The previous node needs to be destroyed.
|
|
32
|
+
if (currNode) {
|
|
33
|
+
currNode.dispose();
|
|
34
|
+
removeChildNode();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (nextJSON) {
|
|
38
|
+
const newNode = this.createChildNode(nextJSON);
|
|
39
|
+
updateChildNode(newNode);
|
|
40
|
+
this.fireChange();
|
|
41
|
+
return newNode;
|
|
42
|
+
} else {
|
|
43
|
+
// Also trigger an update when deleting a child node directly.
|
|
44
|
+
this.fireChange();
|
|
45
|
+
}
|
|
46
|
+
} else if (nextJSON) {
|
|
47
|
+
currNode?.fromJSON(nextJSON);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return currNode;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function parseTypeJsonOrKind(typeJSONOrKind?: ASTNodeJSONOrKind): ASTNodeJSON | undefined {
|
|
54
|
+
return typeof typeJSONOrKind === 'string' ? { kind: typeJSONOrKind } : typeJSONOrKind;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Get all children.
|
|
58
|
+
export function getAllChildren(ast: ASTNode): ASTNode[] {
|
|
59
|
+
return [...ast.children, ...ast.children.map((_child) => getAllChildren(_child)).flat()];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* isMatchAST is same as ASTMatch.is
|
|
64
|
+
* @param node
|
|
65
|
+
* @param targetType
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
export function isMatchAST<TargetASTNode extends ASTNode>(
|
|
69
|
+
node?: ASTNode,
|
|
70
|
+
targetType?: { kind: string; new (...args: any[]): TargetASTNode }
|
|
71
|
+
): node is TargetASTNode {
|
|
72
|
+
return ASTMatch.is(node, targetType);
|
|
73
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { interfaces } from 'inversify';
|
|
7
|
+
|
|
8
|
+
import { type ASTNode } from '../ast-node';
|
|
9
|
+
|
|
10
|
+
export const injectToAST = (serviceIdentifier: interfaces.ServiceIdentifier) =>
|
|
11
|
+
function (target: any, propertyKey: string) {
|
|
12
|
+
if (!serviceIdentifier) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`ServiceIdentifier ${serviceIdentifier} in @lazyInject is Empty, it might be caused by file circular dependency, please check it.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const descriptor = {
|
|
19
|
+
get() {
|
|
20
|
+
const container = (this as ASTNode).scope.variableEngine.container;
|
|
21
|
+
return container.get(serviceIdentifier);
|
|
22
|
+
},
|
|
23
|
+
set() {},
|
|
24
|
+
configurable: true,
|
|
25
|
+
enumerable: true,
|
|
26
|
+
} as any;
|
|
27
|
+
|
|
28
|
+
// Object.defineProperty(target, propertyKey, descriptor);
|
|
29
|
+
|
|
30
|
+
return descriptor;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const POST_CONSTRUCT_AST_SYMBOL = Symbol('post_construct_ast');
|
|
34
|
+
|
|
35
|
+
export const postConstructAST = () => (target: any, propertyKey: string) => {
|
|
36
|
+
// Only run once.
|
|
37
|
+
if (!Reflect.hasMetadata(POST_CONSTRUCT_AST_SYMBOL, target)) {
|
|
38
|
+
Reflect.defineMetadata(POST_CONSTRUCT_AST_SYMBOL, propertyKey, target);
|
|
39
|
+
} else {
|
|
40
|
+
throw Error('Duplication Post Construct AST');
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ASTNodeFlags } from '../flags';
|
|
7
|
+
import { BaseVariableField } from '../declaration';
|
|
8
|
+
import { ASTNode } from '../ast-node';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parent variable fields, sorted from nearest to farthest.
|
|
12
|
+
*/
|
|
13
|
+
export function getParentFields(ast: ASTNode): BaseVariableField[] {
|
|
14
|
+
let curr = ast.parent;
|
|
15
|
+
const res: BaseVariableField[] = [];
|
|
16
|
+
|
|
17
|
+
while (curr) {
|
|
18
|
+
if (curr.flags & ASTNodeFlags.VariableField) {
|
|
19
|
+
res.push(curr as BaseVariableField);
|
|
20
|
+
}
|
|
21
|
+
curr = curr.parent;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return res;
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { ScopeProvider, useCurrentScope, ScopeKey } from './scope-provider';
|
|
7
|
+
export { useScopeAvailable } from './use-scope-available';
|
|
8
|
+
export { useAvailableVariables } from './use-available-variables';
|
|
9
|
+
export { useOutputVariables } from './use-output-variables';
|