ripple 0.2.180 → 0.2.183

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/src/utils/ast.js CHANGED
@@ -1,218 +1,273 @@
1
+ /** @import * as AST from 'estree' */
2
+
3
+ /**
4
+ * Represents the path of a destructured assignment from either a declaration
5
+ * or assignment expression. For example, given `const { foo: { bar: baz } } = quux`,
6
+ * the path of `baz` is `foo.bar`
7
+ * @typedef {{
8
+ * node: AST.Identifier | AST.MemberExpression;
9
+ * is_rest: boolean;
10
+ * has_default_value: boolean;
11
+ * expression: (object: AST.Identifier | AST.CallExpression) => AST.Expression;
12
+ * update_expression: (object: AST.Identifier) => AST.Expression;
13
+ * }} DestructuredAssignment
14
+ * - `node`: The node the destructuring path ends in. Can be a member expression only for assignment expressions
15
+ * - `is_rest`: `true` if this is a `...rest` destructuring
16
+ * - `has_default_value`: `true` if this has a fallback value like `const { foo = 'bar' } = ..`
17
+ * - `expression`: The value of the current path. Will be a call expression if a rest element or default is involved — e.g. `const { foo: { bar: baz = 42 }, ...rest } = quux` — since we can't represent `baz` or `rest` purely as a path. Will be an await expression in case of an async default value (`const { foo = await bar } = ...`)
18
+ * - `update_expression`: Like `expression` but without default values.
19
+ */
20
+
1
21
  import * as b from './builders.js';
2
22
 
23
+ /**
24
+ * Gets the left-most identifier of a member expression or identifier.
25
+ * @param {AST.MemberExpression | AST.Identifier} expression
26
+ * @returns {AST.Identifier | null}
27
+ */
3
28
  export function object(expression) {
4
- while (expression.type === 'MemberExpression') {
5
- expression = /** @type {ESTree.MemberExpression | ESTree.Identifier} */ (expression.object);
6
- }
29
+ while (expression.type === 'MemberExpression') {
30
+ expression = /** @type {AST.MemberExpression | AST.Identifier} */ (expression.object);
31
+ }
7
32
 
8
- if (expression.type !== 'Identifier') {
9
- return null;
10
- }
33
+ if (expression.type !== 'Identifier') {
34
+ return null;
35
+ }
11
36
 
12
- return expression;
37
+ return expression;
13
38
  }
14
39
 
40
+ /**
41
+ * Extracts all identifiers and member expressions from a pattern.
42
+ * @param {AST.Pattern} pattern
43
+ * @param {Array<AST.Identifier | AST.MemberExpression>} [nodes]
44
+ * @returns {Array<AST.Identifier | AST.MemberExpression>}
45
+ */
15
46
  export function unwrap_pattern(pattern, nodes = []) {
16
- switch (pattern.type) {
17
- case 'Identifier':
18
- nodes.push(pattern);
19
- break;
20
-
21
- case 'MemberExpression':
22
- // member expressions can be part of an assignment pattern, but not a binding pattern
23
- // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#binding_and_assignment
24
- nodes.push(pattern);
25
- break;
26
-
27
- case 'ObjectPattern':
28
- for (const prop of pattern.properties) {
29
- if (prop.type === 'RestElement') {
30
- unwrap_pattern(prop.argument, nodes);
31
- } else {
32
- unwrap_pattern(prop.value, nodes);
33
- }
34
- }
35
-
36
- break;
37
-
38
- case 'ArrayPattern':
39
- for (const element of pattern.elements) {
40
- if (element) unwrap_pattern(element, nodes);
41
- }
42
-
43
- break;
44
-
45
- case 'RestElement':
46
- unwrap_pattern(pattern.argument, nodes);
47
- break;
48
-
49
- case 'AssignmentPattern':
50
- unwrap_pattern(pattern.left, nodes);
51
- break;
52
- }
53
-
54
- return nodes;
47
+ switch (pattern.type) {
48
+ case 'Identifier':
49
+ nodes.push(pattern);
50
+ break;
51
+
52
+ case 'MemberExpression':
53
+ // member expressions can be part of an assignment pattern, but not a binding pattern
54
+ // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#binding_and_assignment
55
+ nodes.push(pattern);
56
+ break;
57
+
58
+ case 'ObjectPattern':
59
+ for (const prop of pattern.properties) {
60
+ if (prop.type === 'RestElement') {
61
+ unwrap_pattern(prop.argument, nodes);
62
+ } else {
63
+ unwrap_pattern(prop.value, nodes);
64
+ }
65
+ }
66
+
67
+ break;
68
+
69
+ case 'ArrayPattern':
70
+ for (const element of pattern.elements) {
71
+ if (element) unwrap_pattern(element, nodes);
72
+ }
73
+
74
+ break;
75
+
76
+ case 'RestElement':
77
+ unwrap_pattern(pattern.argument, nodes);
78
+ break;
79
+
80
+ case 'AssignmentPattern':
81
+ unwrap_pattern(pattern.left, nodes);
82
+ break;
83
+ }
84
+
85
+ return nodes;
55
86
  }
56
87
 
88
+ /**
89
+ * Extracts all identifiers from a pattern.
90
+ * @param {AST.Pattern} pattern
91
+ * @returns {AST.Identifier[]}
92
+ */
57
93
  export function extract_identifiers(pattern) {
58
- return unwrap_pattern(pattern, []).filter((node) => node.type === 'Identifier');
94
+ return unwrap_pattern(pattern, []).filter((node) => node.type === 'Identifier');
59
95
  }
60
96
 
97
+ /**
98
+ * Extracts all destructured assignments from a pattern.
99
+ * @param {AST.Node} param
100
+ * @returns {DestructuredAssignment[]}
101
+ */
61
102
  export function extract_paths(param) {
62
- return _extract_paths(
63
- [],
64
- param,
65
- (node) => /** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node),
66
- (node) => /** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node),
67
- false,
68
- );
103
+ return _extract_paths(
104
+ [],
105
+ param,
106
+ /** @param {AST.Identifier | AST.MemberExpression | AST.CallExpression} node */
107
+ (node) => node,
108
+ /** @param {AST.Identifier | AST.MemberExpression} node */
109
+ (node) => node,
110
+ false,
111
+ );
69
112
  }
70
113
 
114
+ /**
115
+ * @param {DestructuredAssignment[]} assignments
116
+ * @param {AST.Node} param
117
+ * @param {DestructuredAssignment['expression']} expression
118
+ * @param {DestructuredAssignment['update_expression']} update_expression
119
+ * @param {boolean} has_default_value
120
+ * @returns {DestructuredAssignment[]}
121
+ */
71
122
  function _extract_paths(assignments = [], param, expression, update_expression, has_default_value) {
72
- switch (param.type) {
73
- case 'Identifier':
74
- case 'MemberExpression':
75
- assignments.push({
76
- node: param,
77
- is_rest: false,
78
- has_default_value,
79
- expression,
80
- update_expression,
81
- });
82
- break;
83
-
84
- case 'ObjectPattern':
85
- for (const prop of param.properties) {
86
- if (prop.type === 'RestElement') {
87
- /** @type {DestructuredAssignment['expression']} */
88
- const rest_expression = (object) => {
89
- /** @type {ESTree.Expression[]} */
90
- const props = [];
91
-
92
- for (const p of param.properties) {
93
- if (p.type === 'Property' && p.key.type !== 'PrivateIdentifier') {
94
- if (p.key.type === 'Identifier' && !p.computed) {
95
- props.push(b.literal(p.key.name));
96
- } else if (p.key.type === 'Literal') {
97
- props.push(b.literal(String(p.key.value)));
98
- } else {
99
- props.push(b.call('String', p.key));
100
- }
101
- }
102
- }
103
-
104
- return b.call('_$_.exclude_from_object', expression(object), b.array(props));
105
- };
106
-
107
- if (prop.argument.type === 'Identifier') {
108
- assignments.push({
109
- node: prop.argument,
110
- is_rest: true,
111
- has_default_value,
112
- expression: rest_expression,
113
- update_expression: rest_expression,
114
- });
115
- } else {
116
- _extract_paths(
117
- assignments,
118
- prop.argument,
119
- rest_expression,
120
- rest_expression,
121
- has_default_value,
122
- );
123
- }
124
- } else {
125
- /** @type {DestructuredAssignment['expression']} */
126
- const object_expression = (object) =>
127
- b.member(expression(object), prop.key, prop.computed || prop.key.type !== 'Identifier');
128
- _extract_paths(
129
- assignments,
130
- prop.value,
131
- object_expression,
132
- object_expression,
133
- has_default_value,
134
- );
135
- }
136
- }
137
-
138
- break;
139
-
140
- case 'ArrayPattern':
141
- for (let i = 0; i < param.elements.length; i += 1) {
142
- const element = param.elements[i];
143
- if (element) {
144
- if (element.type === 'RestElement') {
145
- /** @type {DestructuredAssignment['expression']} */
146
- const rest_expression = (object) =>
147
- b.call(b.member(expression(object), 'slice'), b.literal(i));
148
- if (element.argument.type === 'Identifier') {
149
- assignments.push({
150
- node: element.argument,
151
- is_rest: true,
152
- has_default_value,
153
- expression: rest_expression,
154
- update_expression: rest_expression,
155
- });
156
- } else {
157
- _extract_paths(
158
- assignments,
159
- element.argument,
160
- rest_expression,
161
- rest_expression,
162
- has_default_value,
163
- );
164
- }
165
- } else {
166
- /** @type {DestructuredAssignment['expression']} */
167
- const array_expression = (object) => b.member(expression(object), b.literal(i), true);
168
- _extract_paths(
169
- assignments,
170
- element,
171
- array_expression,
172
- array_expression,
173
- has_default_value,
174
- );
175
- }
176
- }
177
- }
178
-
179
- break;
180
-
181
- case 'AssignmentPattern': {
182
- /** @type {DestructuredAssignment['expression']} */
183
- const fallback_expression = (object) => build_fallback(expression(object), param.right);
184
-
185
- if (param.left.type === 'Identifier') {
186
- assignments.push({
187
- node: param.left,
188
- is_rest: false,
189
- has_default_value: true,
190
- expression: fallback_expression,
191
- update_expression,
192
- });
193
- } else {
194
- _extract_paths(assignments, param.left, fallback_expression, update_expression, true);
195
- }
196
-
197
- break;
198
- }
199
- }
200
-
201
- return assignments;
123
+ switch (param.type) {
124
+ case 'Identifier':
125
+ case 'MemberExpression':
126
+ assignments.push({
127
+ node: param,
128
+ is_rest: false,
129
+ has_default_value,
130
+ expression,
131
+ update_expression,
132
+ });
133
+ break;
134
+
135
+ case 'ObjectPattern':
136
+ for (const prop of param.properties) {
137
+ if (prop.type === 'RestElement') {
138
+ /** @type {DestructuredAssignment['expression']} */
139
+ const rest_expression = (object) => {
140
+ /** @type {AST.Expression[]} */
141
+ const props = [];
142
+
143
+ for (const p of param.properties) {
144
+ if (p.type === 'Property' && p.key.type !== 'PrivateIdentifier') {
145
+ if (p.key.type === 'Identifier' && !p.computed) {
146
+ props.push(b.literal(p.key.name));
147
+ } else if (p.key.type === 'Literal') {
148
+ props.push(b.literal(String(p.key.value)));
149
+ } else {
150
+ props.push(b.call('String', p.key));
151
+ }
152
+ }
153
+ }
154
+
155
+ return b.call('_$_.exclude_from_object', expression(object), b.array(props));
156
+ };
157
+
158
+ if (prop.argument.type === 'Identifier') {
159
+ assignments.push({
160
+ node: prop.argument,
161
+ is_rest: true,
162
+ has_default_value,
163
+ expression: rest_expression,
164
+ update_expression: rest_expression,
165
+ });
166
+ } else {
167
+ _extract_paths(
168
+ assignments,
169
+ prop.argument,
170
+ rest_expression,
171
+ rest_expression,
172
+ has_default_value,
173
+ );
174
+ }
175
+ } else {
176
+ /** @type {DestructuredAssignment['expression']} */
177
+ const object_expression = (object) =>
178
+ b.member(expression(object), prop.key, prop.computed || prop.key.type !== 'Identifier');
179
+ _extract_paths(
180
+ assignments,
181
+ prop.value,
182
+ object_expression,
183
+ object_expression,
184
+ has_default_value,
185
+ );
186
+ }
187
+ }
188
+
189
+ break;
190
+
191
+ case 'ArrayPattern':
192
+ for (let i = 0; i < param.elements.length; i += 1) {
193
+ const element = param.elements[i];
194
+ if (element) {
195
+ if (element.type === 'RestElement') {
196
+ /** @type {DestructuredAssignment['expression']} */
197
+ const rest_expression = (object) =>
198
+ b.call(b.member(expression(object), 'slice'), b.literal(i));
199
+ if (element.argument.type === 'Identifier') {
200
+ assignments.push({
201
+ node: element.argument,
202
+ is_rest: true,
203
+ has_default_value,
204
+ expression: rest_expression,
205
+ update_expression: rest_expression,
206
+ });
207
+ } else {
208
+ _extract_paths(
209
+ assignments,
210
+ element.argument,
211
+ rest_expression,
212
+ rest_expression,
213
+ has_default_value,
214
+ );
215
+ }
216
+ } else {
217
+ /** @type {DestructuredAssignment['expression']} */
218
+ const array_expression = (object) => b.member(expression(object), b.literal(i), true);
219
+ _extract_paths(
220
+ assignments,
221
+ element,
222
+ array_expression,
223
+ array_expression,
224
+ has_default_value,
225
+ );
226
+ }
227
+ }
228
+ }
229
+
230
+ break;
231
+
232
+ case 'AssignmentPattern': {
233
+ /** @type {DestructuredAssignment['expression']} */
234
+ const fallback_expression = (object) => build_fallback(expression(object), param.right);
235
+
236
+ if (param.left.type === 'Identifier') {
237
+ assignments.push({
238
+ node: param.left,
239
+ is_rest: false,
240
+ has_default_value: true,
241
+ expression: fallback_expression,
242
+ update_expression,
243
+ });
244
+ } else {
245
+ _extract_paths(assignments, param.left, fallback_expression, update_expression, true);
246
+ }
247
+
248
+ break;
249
+ }
250
+ }
251
+
252
+ return assignments;
202
253
  }
203
254
 
255
+ /**
256
+ * @param {AST.Expression} expression
257
+ * @param {AST.Expression} fallback
258
+ */
204
259
  export function build_fallback(expression, fallback) {
205
- return b.call('_$_.fallback', expression, fallback);
260
+ return b.call('_$_.fallback', expression, fallback);
206
261
  }
207
262
 
208
263
  /**
209
- * @param {ESTree.AssignmentOperator} operator
210
- * @param {ESTree.Identifier | ESTree.MemberExpression} left
211
- * @param {ESTree.Expression} right
264
+ * @param {AST.AssignmentOperator} operator
265
+ * @param {AST.Identifier | AST.MemberExpression} left
266
+ * @param {AST.Expression} right
212
267
  */
213
268
  export function build_assignment_value(operator, left, right) {
214
- return operator === '='
215
- ? right
216
- : // turn something like x += 1 into x = x + 1
217
- b.binary(/** @type {ESTree.BinaryOperator} */ (operator.slice(0, -1)), left, right);
269
+ return operator === '='
270
+ ? right
271
+ : // turn something like x += 1 into x = x + 1
272
+ b.binary(/** @type {AST.BinaryOperator} */ (operator.slice(0, -1)), left, right);
218
273
  }