ripple 0.2.91 → 0.2.93
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/package.json +1 -1
- package/src/compiler/index.js +17 -10
- package/src/compiler/phases/1-parse/index.js +55 -7
- package/src/compiler/phases/3-transform/client/index.js +82 -55
- package/src/compiler/phases/3-transform/segments.js +422 -224
- package/src/compiler/scope.js +478 -404
- package/src/compiler/types/index.d.ts +299 -3
- package/src/compiler/utils.js +173 -30
- package/src/runtime/index-client.js +1 -0
- package/src/runtime/internal/client/html.js +18 -8
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/portal.js +55 -32
- package/src/runtime/internal/client/render.js +31 -1
- package/src/runtime/internal/client/runtime.js +53 -22
- package/src/utils/normalize_css_property_name.js +23 -0
- package/tests/client/basic.test.ripple +207 -1
- package/tests/client/compiler.test.ripple +95 -1
- package/tests/client/html.test.ripple +29 -1
- package/tests/client/portal.test.ripple +167 -0
- package/types/index.d.ts +4 -0
package/src/compiler/scope.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
/** @import
|
|
1
|
+
/** @import { ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator, Super } from 'estree' */
|
|
2
|
+
/** @import { DeclarationKind, BindingKind, Component, Element } from '#compiler' */
|
|
3
|
+
/** @import { RippleNode } from '#compiler' */
|
|
2
4
|
|
|
3
5
|
import is_reference from 'is-reference';
|
|
4
6
|
import { extract_identifiers, object, unwrap_pattern } from '../utils/ast.js';
|
|
@@ -6,414 +8,486 @@ import { walk } from 'zimmerframe';
|
|
|
6
8
|
import { is_reserved } from './utils.js';
|
|
7
9
|
import * as b from '../utils/builders.js';
|
|
8
10
|
|
|
11
|
+
export class Binding {
|
|
12
|
+
/** @type {Scope} */
|
|
13
|
+
scope;
|
|
14
|
+
|
|
15
|
+
/** @type {Identifier} */
|
|
16
|
+
node;
|
|
17
|
+
|
|
18
|
+
/** @type {BindingKind} */
|
|
19
|
+
kind;
|
|
20
|
+
|
|
21
|
+
/** @type {DeclarationKind} */
|
|
22
|
+
declaration_kind;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* What the value was initialized with
|
|
26
|
+
* @type {null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration}
|
|
27
|
+
*/
|
|
28
|
+
initial = null;
|
|
29
|
+
|
|
30
|
+
/** @type {Array<{ node: Identifier; path: RippleNode[] }>} */
|
|
31
|
+
references = [];
|
|
32
|
+
|
|
33
|
+
mutated = false;
|
|
34
|
+
reassigned = false;
|
|
35
|
+
is_called = false;
|
|
36
|
+
metadata = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {Scope} scope
|
|
40
|
+
* @param {Identifier} node
|
|
41
|
+
* @param {BindingKind} kind
|
|
42
|
+
* @param {DeclarationKind} declaration_kind
|
|
43
|
+
* @param {Binding['initial']} initial
|
|
44
|
+
*/
|
|
45
|
+
constructor(scope, node, kind, declaration_kind, initial) {
|
|
46
|
+
this.scope = scope;
|
|
47
|
+
this.node = node;
|
|
48
|
+
this.initial = initial;
|
|
49
|
+
this.kind = kind;
|
|
50
|
+
this.declaration_kind = declaration_kind;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get updated() {
|
|
54
|
+
return this.mutated || this.reassigned;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create scopes for an AST
|
|
60
|
+
* @param {Node} ast - The AST to create scopes for
|
|
61
|
+
* @param {ScopeRoot} root - Root scope manager
|
|
62
|
+
* @param {Scope | null} parent - Parent scope
|
|
63
|
+
* @returns {{ scope: Scope, scopes: Map<RippleNode, Scope> }} Scope information
|
|
64
|
+
*/
|
|
9
65
|
export function create_scopes(ast, root, parent) {
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
66
|
+
/** @typedef {{ scope: Scope }} State */
|
|
67
|
+
|
|
68
|
+
/** @type {Map<RippleNode, Scope>} */
|
|
69
|
+
const scopes = new Map();
|
|
70
|
+
const scope = new Scope(root, parent, false);
|
|
71
|
+
scopes.set(ast, scope);
|
|
72
|
+
|
|
73
|
+
/** @type {State} */
|
|
74
|
+
const state = { scope };
|
|
75
|
+
/** @type {Array<[Scope, { node: Identifier, path: RippleNode[] }]>} */
|
|
76
|
+
const references = [];
|
|
77
|
+
/** @type {Array<[Scope, Pattern | MemberExpression]>} */
|
|
78
|
+
const updates = [];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Add parameters to scope
|
|
82
|
+
* @param {Scope} scope - The scope to add parameters to
|
|
83
|
+
* @param {Pattern[]} params - Parameter nodes
|
|
84
|
+
*/
|
|
85
|
+
function add_params(scope, params) {
|
|
86
|
+
for (const param of params) {
|
|
87
|
+
for (const node of extract_identifiers(param)) {
|
|
88
|
+
scope.declare(node, 'normal', param.type === 'RestElement' ? 'rest_param' : 'param');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create a block scope
|
|
95
|
+
* @param {any} node - AST node
|
|
96
|
+
* @param {{ state: any, next: Function }} context - Visitor context
|
|
97
|
+
*/
|
|
98
|
+
const create_block_scope = (node, { state, next }) => {
|
|
99
|
+
const scope = state.scope.child(true);
|
|
100
|
+
scopes.set(node, scope);
|
|
101
|
+
|
|
102
|
+
if (node.type === 'ForOfStatement') {
|
|
103
|
+
if (node.index) {
|
|
104
|
+
state.scope.declare(node.index, 'normal', 'let');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
next({ scope });
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
walk(/** @type {RippleNode} */ (ast), state, {
|
|
112
|
+
// references
|
|
113
|
+
Identifier(node, { path, state }) {
|
|
114
|
+
const parent = path.at(-1);
|
|
115
|
+
if (
|
|
116
|
+
parent &&
|
|
117
|
+
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
118
|
+
// TSTypeAnnotation, TSInterfaceDeclaration etc - these are normally already filtered out,
|
|
119
|
+
// but for the migration they aren't, so we need to filter them out here
|
|
120
|
+
// TODO -> once migration script is gone we can remove this check
|
|
121
|
+
!parent.type.startsWith('TS')
|
|
122
|
+
) {
|
|
123
|
+
references.push([state.scope, { node, path: path.slice() }]);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
AssignmentExpression(node, { state, next }) {
|
|
128
|
+
updates.push([state.scope, node.left]);
|
|
129
|
+
next();
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
UpdateExpression(node, { state, next }) {
|
|
133
|
+
updates.push([state.scope, /** @type {Identifier | MemberExpression} */ (node.argument)]);
|
|
134
|
+
next();
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
ImportDeclaration(node, { state }) {
|
|
138
|
+
for (const specifier of node.specifiers) {
|
|
139
|
+
state.scope.declare(specifier.local, 'normal', 'import', node);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {Component} node
|
|
145
|
+
* @param {Object} context
|
|
146
|
+
* @param {any} context.state
|
|
147
|
+
* @param {Function} context.next
|
|
148
|
+
*/
|
|
149
|
+
Component(node, { state, next }) {
|
|
150
|
+
const scope = state.scope.child();
|
|
151
|
+
scopes.set(node, scope);
|
|
152
|
+
|
|
153
|
+
scope.declare(node.id, 'normal', 'component');
|
|
154
|
+
|
|
155
|
+
add_params(scope, node.params);
|
|
156
|
+
next({ scope });
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {Element} node
|
|
161
|
+
* @param {Object} context
|
|
162
|
+
* @param {any} context.state
|
|
163
|
+
* @param {Function} context.next
|
|
164
|
+
*/
|
|
165
|
+
Element(node, { state, next }) {
|
|
166
|
+
const scope = state.scope.child();
|
|
167
|
+
scopes.set(node, scope);
|
|
168
|
+
|
|
169
|
+
next({ scope });
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
FunctionExpression(node, { state, next }) {
|
|
173
|
+
const scope = state.scope.child();
|
|
174
|
+
scopes.set(node, scope);
|
|
175
|
+
|
|
176
|
+
if (node.id) scope.declare(node.id, 'normal', 'function');
|
|
177
|
+
|
|
178
|
+
add_params(scope, node.params);
|
|
179
|
+
next({ scope });
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
FunctionDeclaration(node, { state, next }) {
|
|
183
|
+
if (node.id) state.scope.declare(node.id, 'normal', 'function', node);
|
|
184
|
+
|
|
185
|
+
const scope = state.scope.child();
|
|
186
|
+
scopes.set(node, scope);
|
|
187
|
+
|
|
188
|
+
add_params(scope, node.params);
|
|
189
|
+
next({ scope });
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
ArrowFunctionExpression(node, { state, next }) {
|
|
193
|
+
const scope = state.scope.child();
|
|
194
|
+
scopes.set(node, scope);
|
|
195
|
+
|
|
196
|
+
add_params(scope, node.params);
|
|
197
|
+
next({ scope });
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
ForStatement: create_block_scope,
|
|
201
|
+
ForInStatement: create_block_scope,
|
|
202
|
+
ForOfStatement: create_block_scope,
|
|
203
|
+
SwitchStatement: create_block_scope,
|
|
204
|
+
BlockStatement(node, context) {
|
|
205
|
+
const parent = context.path.at(-1);
|
|
206
|
+
if (
|
|
207
|
+
parent?.type === 'FunctionDeclaration' ||
|
|
208
|
+
parent?.type === 'FunctionExpression' ||
|
|
209
|
+
parent?.type === 'ArrowFunctionExpression'
|
|
210
|
+
) {
|
|
211
|
+
// We already created a new scope for the function
|
|
212
|
+
context.next();
|
|
213
|
+
} else {
|
|
214
|
+
create_block_scope(node, context);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
ClassDeclaration(node, { state, next }) {
|
|
219
|
+
if (node.id) state.scope.declare(node.id, 'normal', 'let', node);
|
|
220
|
+
next();
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
VariableDeclaration(node, { state, path, next }) {
|
|
224
|
+
for (const declarator of node.declarations) {
|
|
225
|
+
/** @type {Binding[]} */
|
|
226
|
+
const bindings = [];
|
|
227
|
+
|
|
228
|
+
state.scope.declarators.set(declarator, bindings);
|
|
229
|
+
|
|
230
|
+
for (const id of extract_identifiers(declarator.id)) {
|
|
231
|
+
const binding = state.scope.declare(id, 'normal', node.kind, declarator.init);
|
|
232
|
+
bindings.push(binding);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
next();
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
CatchClause(node, { state, next }) {
|
|
240
|
+
if (node.param) {
|
|
241
|
+
const scope = state.scope.child(true);
|
|
242
|
+
scopes.set(node, scope);
|
|
243
|
+
|
|
244
|
+
for (const id of extract_identifiers(node.param)) {
|
|
245
|
+
scope.declare(id, 'normal', 'let');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
next({ scope });
|
|
249
|
+
} else {
|
|
250
|
+
next();
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
for (const [scope, { node, path }] of references) {
|
|
256
|
+
scope.reference(node, path);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const [scope, node] of updates) {
|
|
260
|
+
for (const expression of unwrap_pattern(node)) {
|
|
261
|
+
const left = object(expression);
|
|
262
|
+
const binding = left && scope.get(left.name);
|
|
263
|
+
|
|
264
|
+
if (binding !== null && left !== binding.node) {
|
|
265
|
+
binding.updated = true;
|
|
266
|
+
|
|
267
|
+
if (left === expression) {
|
|
268
|
+
binding.reassigned = true;
|
|
269
|
+
} else {
|
|
270
|
+
binding.mutated = true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
scope,
|
|
278
|
+
scopes,
|
|
279
|
+
};
|
|
206
280
|
}
|
|
207
281
|
|
|
208
282
|
export class Scope {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
283
|
+
/** @type {ScopeRoot} */
|
|
284
|
+
root;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The immediate parent scope
|
|
288
|
+
* @type {Scope | null}
|
|
289
|
+
*/
|
|
290
|
+
parent;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Whether or not `var` declarations are contained by this scope
|
|
294
|
+
* @type {boolean}
|
|
295
|
+
*/
|
|
296
|
+
#porous;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* A map of every identifier declared by this scope, and all the
|
|
300
|
+
* identifiers that reference it
|
|
301
|
+
* @type {Map<string, Binding>}
|
|
302
|
+
*/
|
|
303
|
+
declarations = new Map();
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* A map of declarators to the bindings they declare
|
|
307
|
+
* @type {Map<VariableDeclarator, Binding[]>}
|
|
308
|
+
*/
|
|
309
|
+
declarators = new Map();
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* A set of all the names referenced with this scope
|
|
313
|
+
* — useful for generating unique names
|
|
314
|
+
* @type {Map<string, { node: Identifier; path: RippleNode[] }[]>}
|
|
315
|
+
*/
|
|
316
|
+
references = new Map();
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The scope depth allows us to determine if a state variable is referenced in its own scope,
|
|
320
|
+
* which is usually an error. Block statements do not increase this value
|
|
321
|
+
*/
|
|
322
|
+
function_depth = 0;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* If tracing of reactive dependencies is enabled for this scope
|
|
326
|
+
* @type {null | Expression}
|
|
327
|
+
*/
|
|
328
|
+
tracing = null;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
*
|
|
332
|
+
* @param {ScopeRoot} root
|
|
333
|
+
* @param {Scope | null} parent
|
|
334
|
+
* @param {boolean} porous
|
|
335
|
+
*/
|
|
336
|
+
constructor(root, parent, porous) {
|
|
337
|
+
this.root = root;
|
|
338
|
+
this.parent = parent;
|
|
339
|
+
this.#porous = porous;
|
|
340
|
+
this.function_depth = parent ? parent.function_depth + (porous ? 0 : 1) : 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* @param {Identifier} node
|
|
345
|
+
* @param {Binding['kind']} kind
|
|
346
|
+
* @param {DeclarationKind} declaration_kind
|
|
347
|
+
* @param {null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration} initial
|
|
348
|
+
* @returns {Binding}
|
|
349
|
+
*/
|
|
350
|
+
declare(node, kind, declaration_kind, initial = null) {
|
|
351
|
+
if (this.parent) {
|
|
352
|
+
if (declaration_kind === 'var' && this.#porous) {
|
|
353
|
+
return this.parent.declare(node, kind, declaration_kind);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (declaration_kind === 'import') {
|
|
357
|
+
return this.parent.declare(node, kind, declaration_kind, initial);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (node.name === '_$_') {
|
|
362
|
+
throw new Error('Cannot declare a variable named "_$_" as it is a reserved identifier');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (this.declarations.has(node.name)) {
|
|
366
|
+
throw new Error(`'${node.name}' has already been declared in the current scope`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** @type {Binding} */
|
|
370
|
+
const binding = {
|
|
371
|
+
node,
|
|
372
|
+
references: [],
|
|
373
|
+
initial,
|
|
374
|
+
reassigned: false,
|
|
375
|
+
mutated: false,
|
|
376
|
+
updated: false,
|
|
377
|
+
scope: this,
|
|
378
|
+
kind,
|
|
379
|
+
declaration_kind,
|
|
380
|
+
is_called: false,
|
|
381
|
+
metadata: null,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
this.declarations.set(node.name, binding);
|
|
385
|
+
this.root.conflicts.add(node.name);
|
|
386
|
+
return binding;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
child(porous = false) {
|
|
390
|
+
return new Scope(this.root, this, porous);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @param {string} preferred_name
|
|
395
|
+
* @returns {string}
|
|
396
|
+
*/
|
|
397
|
+
generate(preferred_name) {
|
|
398
|
+
if (this.#porous) {
|
|
399
|
+
return /** @type {Scope} */ (this.parent).generate(preferred_name);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
preferred_name = preferred_name.replace(/[^a-zA-Z0-9_$]/g, '_').replace(/^[0-9]/, '_');
|
|
403
|
+
let name = preferred_name;
|
|
404
|
+
let n = 1;
|
|
405
|
+
|
|
406
|
+
while (
|
|
407
|
+
this.references.has(name) ||
|
|
408
|
+
this.declarations.has(name) ||
|
|
409
|
+
this.root.conflicts.has(name) ||
|
|
410
|
+
is_reserved(name)
|
|
411
|
+
) {
|
|
412
|
+
name = `${preferred_name}_${n++}`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
this.references.set(name, []);
|
|
416
|
+
this.root.conflicts.add(name);
|
|
417
|
+
return name;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @param {string} name
|
|
422
|
+
* @returns {Binding | null}
|
|
423
|
+
*/
|
|
424
|
+
get(name) {
|
|
425
|
+
return this.declarations.get(name) ?? this.parent?.get(name) ?? null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* @param {VariableDeclarator} node
|
|
430
|
+
* @returns {Binding[]}
|
|
431
|
+
*/
|
|
432
|
+
get_bindings(node) {
|
|
433
|
+
const bindings = this.declarators.get(node);
|
|
434
|
+
if (!bindings) {
|
|
435
|
+
throw new Error('No binding found for declarator');
|
|
436
|
+
}
|
|
437
|
+
return bindings;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* @param {string} name
|
|
442
|
+
* @returns {Scope | null}
|
|
443
|
+
*/
|
|
444
|
+
owner(name) {
|
|
445
|
+
return this.declarations.has(name) ? this : this.parent && this.parent.owner(name);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @param {Identifier} node
|
|
450
|
+
* @param {RippleNode[]} path
|
|
451
|
+
*/
|
|
452
|
+
reference(node, path) {
|
|
453
|
+
path = [...path]; // ensure that mutations to path afterwards don't affect this reference
|
|
454
|
+
let references = this.references.get(node.name);
|
|
455
|
+
|
|
456
|
+
if (!references) this.references.set(node.name, (references = []));
|
|
457
|
+
|
|
458
|
+
references.push({ node, path });
|
|
459
|
+
|
|
460
|
+
const binding = this.declarations.get(node.name);
|
|
461
|
+
if (binding) {
|
|
462
|
+
binding.references.push({ node, path });
|
|
463
|
+
} else if (this.parent) {
|
|
464
|
+
this.parent.reference(node, path);
|
|
465
|
+
} else {
|
|
466
|
+
// no binding was found, and this is the top level scope,
|
|
467
|
+
// which means this is a global
|
|
468
|
+
this.root.conflicts.add(node.name);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
397
471
|
}
|
|
398
472
|
|
|
399
473
|
export class ScopeRoot {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
474
|
+
/** @type {Set<string>} */
|
|
475
|
+
conflicts = new Set();
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* @param {string} preferred_name
|
|
479
|
+
*/
|
|
480
|
+
unique(preferred_name) {
|
|
481
|
+
preferred_name = preferred_name.replace(/[^a-zA-Z0-9_$]/g, '_');
|
|
482
|
+
let final_name = preferred_name;
|
|
483
|
+
let n = 1;
|
|
484
|
+
|
|
485
|
+
while (this.conflicts.has(final_name)) {
|
|
486
|
+
final_name = `${preferred_name}_${n++}`;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
this.conflicts.add(final_name);
|
|
490
|
+
const id = b.id(final_name);
|
|
491
|
+
return id;
|
|
492
|
+
}
|
|
419
493
|
}
|