@angular-wave/angular.ts 0.0.50 → 0.0.51

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.
@@ -1,13 +1,9 @@
1
- import {
2
- assignableAST,
3
- findConstantAndWatchExpressions,
4
- getInputs,
5
- getStringValue,
6
- plusFn,
7
- } from "./shared";
8
- import { forEach, isDefined } from "../../shared/utils";
1
+ import { isDefined } from "../../shared/utils";
9
2
  import { ASTType } from "./ast-type";
10
3
 
4
+ export const PURITY_ABSOLUTE = 1;
5
+ export const PURITY_RELATIVE = 2;
6
+
11
7
  export class ASTInterpreter {
12
8
  /**
13
9
  * @param {function(any):any} $filter
@@ -19,49 +15,53 @@ export class ASTInterpreter {
19
15
  /**
20
16
  * Compiles the AST into a function.
21
17
  * @param {import("./ast").ASTNode} ast - The AST to compile.
22
- * @returns
18
+ * @returns {import("./parse").CompiledExpression}
23
19
  */
24
20
  compile(ast) {
25
- const self = this;
26
- findConstantAndWatchExpressions(ast, self.$filter);
21
+ let decoratedNode = findConstantAndWatchExpressions(ast, this.$filter);
22
+ /** @type {import("./ast").ASTNode} */
27
23
  let assignable;
24
+ /** @type {import("./parse").CompiledExpression} */
28
25
  let assign;
29
- if ((assignable = assignableAST(ast))) {
30
- assign = this.recurse(assignable);
26
+ if ((assignable = assignableAST(decoratedNode))) {
27
+ assign = /** @type {import("./parse").CompiledExpression} */ (
28
+ this.recurse(assignable)
29
+ );
31
30
  }
32
- const toWatch = getInputs(ast.body);
31
+ const toWatch = getInputs(decoratedNode.body);
33
32
  let inputs;
34
33
  if (toWatch) {
35
34
  inputs = [];
36
- forEach(toWatch, (watch, key) => {
37
- const input = self.recurse(watch);
35
+ for (const [key, watch] of Object.entries(toWatch)) {
36
+ const input = /** @type {import("./parse").CompiledExpression} */ (
37
+ this.recurse(watch)
38
+ );
38
39
  input.isPure = watch.isPure;
39
40
  watch.input = input;
40
41
  inputs.push(input);
41
42
  watch.watchId = key;
42
- });
43
+ }
43
44
  }
44
45
  const expressions = [];
45
- forEach(ast.body, (expression) => {
46
- expressions.push(self.recurse(expression.expression));
46
+ decoratedNode.body.forEach((expression) => {
47
+ expressions.push(this.recurse(expression.expression));
47
48
  });
48
49
 
50
+ /** @type {import("./parse").CompiledExpression} */
49
51
  const fn =
50
- ast.body.length === 0
52
+ decoratedNode.body.length === 0
51
53
  ? () => {}
52
- : ast.body.length === 1
54
+ : decoratedNode.body.length === 1
53
55
  ? expressions[0]
54
56
  : function (scope, locals) {
55
57
  let lastValue;
56
- forEach(expressions, (exp) => {
58
+ expressions.forEach((exp) => {
57
59
  lastValue = exp(scope, locals);
58
60
  });
59
61
  return lastValue;
60
62
  };
61
63
  if (assign) {
62
- fn.assign = function (scope, value, locals) {
63
- return assign(scope, locals, value);
64
- };
64
+ fn.assign = (scope, value, locals) => assign(scope, locals, value);
65
65
  }
66
66
  if (inputs) {
67
67
  fn.inputs = inputs;
@@ -73,17 +73,14 @@ export class ASTInterpreter {
73
73
  * Recurses the AST nodes.
74
74
  * @param {import("./ast").ASTNode} ast - The AST node.
75
75
  * @param {Object} [context] - The context.
76
- * @param {boolean|number} [create] - The create flag.
77
- * @returns {function} The recursive function.
76
+ * @param {boolean|1} [create] - The create flag.
77
+ * @returns {import("./parse").CompiledExpressionFunction} The recursive function.
78
78
  */
79
79
  recurse(ast, context, create) {
80
80
  let left;
81
81
  let right;
82
82
  const self = this;
83
83
  let args;
84
- if (ast.input) {
85
- return this.inputs(ast.input, ast.watchId);
86
- }
87
84
  switch (ast.type) {
88
85
  case ASTType.Literal:
89
86
  return this.value(ast.value, context);
@@ -114,33 +111,43 @@ export class ASTInterpreter {
114
111
  }
115
112
  if (ast.computed) right = this.recurse(ast.property);
116
113
  return ast.computed
117
- ? this.computedMember(left, right, context, create)
118
- : this.nonComputedMember(left, right, context, create);
114
+ ? this.computedMember(
115
+ left,
116
+ /** @type {function } */ (right),
117
+ context,
118
+ create,
119
+ )
120
+ : this.nonComputedMember(
121
+ left,
122
+ /** @type {string } */ (right),
123
+ context,
124
+ create,
125
+ );
119
126
  case ASTType.CallExpression:
120
127
  args = [];
121
- forEach(ast.arguments, (expr) => {
128
+ ast.arguments.forEach((expr) => {
122
129
  args.push(self.recurse(expr));
123
130
  });
124
131
  if (ast.filter) right = this.$filter(ast.callee.name);
125
132
  if (!ast.filter) right = this.recurse(ast.callee, true);
126
133
  return ast.filter
127
- ? function (scope, locals, assign, inputs) {
134
+ ? function (scope, locals, assign) {
128
135
  const values = [];
129
136
  for (let i = 0; i < args.length; ++i) {
130
- values.push(args[i](scope, locals, assign, inputs));
137
+ values.push(args[i](scope, locals, assign));
131
138
  }
132
- const value = right.apply(undefined, values, inputs);
139
+ const value = right.apply(undefined, values);
133
140
  return context
134
141
  ? { context: undefined, name: undefined, value }
135
142
  : value;
136
143
  }
137
- : function (scope, locals, assign, inputs) {
138
- const rhs = right(scope, locals, assign, inputs);
144
+ : function (scope, locals, assign) {
145
+ const rhs = right(scope, locals, assign);
139
146
  let value;
140
147
  if (rhs.value != null) {
141
148
  const values = [];
142
149
  for (let i = 0; i < args.length; ++i) {
143
- values.push(args[i](scope, locals, assign, inputs));
150
+ values.push(args[i](scope, locals, assign));
144
151
  }
145
152
  value = rhs.value.apply(rhs.context, values);
146
153
  }
@@ -149,27 +156,27 @@ export class ASTInterpreter {
149
156
  case ASTType.AssignmentExpression:
150
157
  left = this.recurse(ast.left, true, 1);
151
158
  right = this.recurse(ast.right);
152
- return function (scope, locals, assign, inputs) {
153
- const lhs = left(scope, locals, assign, inputs);
154
- const rhs = right(scope, locals, assign, inputs);
159
+ return function (scope, locals, assign) {
160
+ const lhs = left(scope, locals, assign);
161
+ const rhs = right(scope, locals, assign);
155
162
  lhs.context[lhs.name] = rhs;
156
163
  return context ? { value: rhs } : rhs;
157
164
  };
158
165
  case ASTType.ArrayExpression:
159
166
  args = [];
160
- forEach(ast.elements, (expr) => {
167
+ ast.elements.forEach((expr) => {
161
168
  args.push(self.recurse(expr));
162
169
  });
163
- return function (scope, locals, assign, inputs) {
170
+ return function (scope, locals, assign) {
164
171
  const value = [];
165
172
  for (let i = 0; i < args.length; ++i) {
166
- value.push(args[i](scope, locals, assign, inputs));
173
+ value.push(args[i](scope, locals, assign));
167
174
  }
168
175
  return context ? { value } : value;
169
176
  };
170
177
  case ASTType.ObjectExpression:
171
178
  args = [];
172
- forEach(ast.properties, (property) => {
179
+ ast.properties.forEach((property) => {
173
180
  if (property.computed) {
174
181
  args.push({
175
182
  key: self.recurse(property.key),
@@ -187,18 +194,17 @@ export class ASTInterpreter {
187
194
  });
188
195
  }
189
196
  });
190
- return function (scope, locals, assign, inputs) {
197
+ return function (scope, locals, assign) {
191
198
  const value = {};
192
199
  for (let i = 0; i < args.length; ++i) {
193
200
  if (args[i].computed) {
194
- value[args[i].key(scope, locals, assign, inputs)] = args[i].value(
201
+ value[args[i].key(scope, locals, assign)] = args[i].value(
195
202
  scope,
196
203
  locals,
197
204
  assign,
198
- inputs,
199
205
  );
200
206
  } else {
201
- value[args[i].key] = args[i].value(scope, locals, assign, inputs);
207
+ value[args[i].key] = args[i].value(scope, locals, assign);
202
208
  }
203
209
  }
204
210
  return context ? { value } : value;
@@ -225,8 +231,8 @@ export class ASTInterpreter {
225
231
  * @returns {function} The unary plus function.
226
232
  */
227
233
  "unary+"(argument, context) {
228
- return function (scope, locals, assign, inputs) {
229
- let arg = argument(scope, locals, assign, inputs);
234
+ return function (scope, locals, assign) {
235
+ let arg = argument(scope, locals, assign);
230
236
  if (isDefined(arg)) {
231
237
  arg = +arg;
232
238
  } else {
@@ -243,8 +249,8 @@ export class ASTInterpreter {
243
249
  * @returns {function} The unary minus function.
244
250
  */
245
251
  "unary-"(argument, context) {
246
- return function (scope, locals, assign, inputs) {
247
- let arg = argument(scope, locals, assign, inputs);
252
+ return function (scope, locals, assign) {
253
+ let arg = argument(scope, locals, assign);
248
254
  if (isDefined(arg)) {
249
255
  arg = -arg;
250
256
  } else {
@@ -261,8 +267,8 @@ export class ASTInterpreter {
261
267
  * @returns {function} The unary negation function.
262
268
  */
263
269
  "unary!"(argument, context) {
264
- return function (scope, locals, assign, inputs) {
265
- const arg = !argument(scope, locals, assign, inputs);
270
+ return function (scope, locals, assign) {
271
+ const arg = !argument(scope, locals, assign);
266
272
  return context ? { value: arg } : arg;
267
273
  };
268
274
  }
@@ -275,9 +281,9 @@ export class ASTInterpreter {
275
281
  * @returns {function} The binary plus function.
276
282
  */
277
283
  "binary+"(left, right, context) {
278
- return function (scope, locals, assign, inputs) {
279
- const lhs = left(scope, locals, assign, inputs);
280
- const rhs = right(scope, locals, assign, inputs);
284
+ return function (scope, locals, assign) {
285
+ const lhs = left(scope, locals, assign);
286
+ const rhs = right(scope, locals, assign);
281
287
  const arg = plusFn(lhs, rhs);
282
288
  return context ? { value: arg } : arg;
283
289
  };
@@ -291,9 +297,9 @@ export class ASTInterpreter {
291
297
  * @returns {function} The binary minus function.
292
298
  */
293
299
  "binary-"(left, right, context) {
294
- return function (scope, locals, assign, inputs) {
295
- const lhs = left(scope, locals, assign, inputs);
296
- const rhs = right(scope, locals, assign, inputs);
300
+ return function (scope, locals, assign) {
301
+ const lhs = left(scope, locals, assign);
302
+ const rhs = right(scope, locals, assign);
297
303
  const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
298
304
  return context ? { value: arg } : arg;
299
305
  };
@@ -307,19 +313,15 @@ export class ASTInterpreter {
307
313
  * @returns {function} The binary multiplication function.
308
314
  */
309
315
  "binary*"(left, right, context) {
310
- return function (scope, locals, assign, inputs) {
311
- const arg =
312
- left(scope, locals, assign, inputs) *
313
- right(scope, locals, assign, inputs);
316
+ return function (scope, locals, assign) {
317
+ const arg = left(scope, locals, assign) * right(scope, locals, assign);
314
318
  return context ? { value: arg } : arg;
315
319
  };
316
320
  }
317
321
 
318
322
  "binary/"(left, right, context) {
319
- return function (scope, locals, assign, inputs) {
320
- const arg =
321
- left(scope, locals, assign, inputs) /
322
- right(scope, locals, assign, inputs);
323
+ return function (scope, locals, assign) {
324
+ const arg = left(scope, locals, assign) / right(scope, locals, assign);
323
325
  return context ? { value: arg } : arg;
324
326
  };
325
327
  }
@@ -332,10 +334,8 @@ export class ASTInterpreter {
332
334
  * @returns {function} The binary division function.
333
335
  */
334
336
  "binary%"(left, right, context) {
335
- return function (scope, locals, assign, inputs) {
336
- const arg =
337
- left(scope, locals, assign, inputs) %
338
- right(scope, locals, assign, inputs);
337
+ return function (scope, locals, assign) {
338
+ const arg = left(scope, locals, assign) % right(scope, locals, assign);
339
339
  return context ? { value: arg } : arg;
340
340
  };
341
341
  }
@@ -348,10 +348,8 @@ export class ASTInterpreter {
348
348
  * @returns {function} The binary strict equality function.
349
349
  */
350
350
  "binary==="(left, right, context) {
351
- return function (scope, locals, assign, inputs) {
352
- const arg =
353
- left(scope, locals, assign, inputs) ===
354
- right(scope, locals, assign, inputs);
351
+ return function (scope, locals, assign) {
352
+ const arg = left(scope, locals, assign) === right(scope, locals, assign);
355
353
  return context ? { value: arg } : arg;
356
354
  };
357
355
  }
@@ -364,10 +362,8 @@ export class ASTInterpreter {
364
362
  * @returns {function} The binary strict inequality function.
365
363
  */
366
364
  "binary!=="(left, right, context) {
367
- return function (scope, locals, assign, inputs) {
368
- const arg =
369
- left(scope, locals, assign, inputs) !==
370
- right(scope, locals, assign, inputs);
365
+ return function (scope, locals, assign) {
366
+ const arg = left(scope, locals, assign) !== right(scope, locals, assign);
371
367
  return context ? { value: arg } : arg;
372
368
  };
373
369
  }
@@ -380,10 +376,8 @@ export class ASTInterpreter {
380
376
  * @returns {function} The binary equality function.
381
377
  */
382
378
  "binary=="(left, right, context) {
383
- return function (scope, locals, assign, inputs) {
384
- const arg =
385
- left(scope, locals, assign, inputs) ==
386
- right(scope, locals, assign, inputs);
379
+ return function (scope, locals, assign) {
380
+ const arg = left(scope, locals, assign) == right(scope, locals, assign);
387
381
  return context ? { value: arg } : arg;
388
382
  };
389
383
  }
@@ -396,10 +390,8 @@ export class ASTInterpreter {
396
390
  * @returns {function} The binary inequality function.
397
391
  */
398
392
  "binary!="(left, right, context) {
399
- return function (scope, locals, assign, inputs) {
400
- const arg =
401
- left(scope, locals, assign, inputs) !=
402
- right(scope, locals, assign, inputs);
393
+ return function (scope, locals, assign) {
394
+ const arg = left(scope, locals, assign) != right(scope, locals, assign);
403
395
  return context ? { value: arg } : arg;
404
396
  };
405
397
  }
@@ -412,10 +404,8 @@ export class ASTInterpreter {
412
404
  * @returns {function} The binary less-than function.
413
405
  */
414
406
  "binary<"(left, right, context) {
415
- return function (scope, locals, assign, inputs) {
416
- const arg =
417
- left(scope, locals, assign, inputs) <
418
- right(scope, locals, assign, inputs);
407
+ return function (scope, locals, assign) {
408
+ const arg = left(scope, locals, assign) < right(scope, locals, assign);
419
409
  return context ? { value: arg } : arg;
420
410
  };
421
411
  }
@@ -428,10 +418,8 @@ export class ASTInterpreter {
428
418
  * @returns {function} The binary greater-than function.
429
419
  */
430
420
  "binary>"(left, right, context) {
431
- return function (scope, locals, assign, inputs) {
432
- const arg =
433
- left(scope, locals, assign, inputs) >
434
- right(scope, locals, assign, inputs);
421
+ return function (scope, locals, assign) {
422
+ const arg = left(scope, locals, assign) > right(scope, locals, assign);
435
423
  return context ? { value: arg } : arg;
436
424
  };
437
425
  }
@@ -444,10 +432,8 @@ export class ASTInterpreter {
444
432
  * @returns {function} The binary less-than-or-equal-to function.
445
433
  */
446
434
  "binary<="(left, right, context) {
447
- return function (scope, locals, assign, inputs) {
448
- const arg =
449
- left(scope, locals, assign, inputs) <=
450
- right(scope, locals, assign, inputs);
435
+ return function (scope, locals, assign) {
436
+ const arg = left(scope, locals, assign) <= right(scope, locals, assign);
451
437
  return context ? { value: arg } : arg;
452
438
  };
453
439
  }
@@ -460,10 +446,8 @@ export class ASTInterpreter {
460
446
  * @returns {function} The binary greater-than-or-equal-to function.
461
447
  */
462
448
  "binary>="(left, right, context) {
463
- return function (scope, locals, assign, inputs) {
464
- const arg =
465
- left(scope, locals, assign, inputs) >=
466
- right(scope, locals, assign, inputs);
449
+ return function (scope, locals, assign) {
450
+ const arg = left(scope, locals, assign) >= right(scope, locals, assign);
467
451
  return context ? { value: arg } : arg;
468
452
  };
469
453
  }
@@ -475,10 +459,8 @@ export class ASTInterpreter {
475
459
  * @returns {function} The binary logical AND function.
476
460
  */
477
461
  "binary&&"(left, right, context) {
478
- return function (scope, locals, assign, inputs) {
479
- const arg =
480
- left(scope, locals, assign, inputs) &&
481
- right(scope, locals, assign, inputs);
462
+ return function (scope, locals, assign) {
463
+ const arg = left(scope, locals, assign) && right(scope, locals, assign);
482
464
  return context ? { value: arg } : arg;
483
465
  };
484
466
  }
@@ -491,10 +473,8 @@ export class ASTInterpreter {
491
473
  * @returns {function} The binary logical OR function.
492
474
  */
493
475
  "binary||"(left, right, context) {
494
- return function (scope, locals, assign, inputs) {
495
- const arg =
496
- left(scope, locals, assign, inputs) ||
497
- right(scope, locals, assign, inputs);
476
+ return function (scope, locals, assign) {
477
+ const arg = left(scope, locals, assign) || right(scope, locals, assign);
498
478
  return context ? { value: arg } : arg;
499
479
  };
500
480
  }
@@ -508,10 +488,10 @@ export class ASTInterpreter {
508
488
  * @returns {function} The ternary conditional function.
509
489
  */
510
490
  "ternary?:"(test, alternate, consequent, context) {
511
- return function (scope, locals, assign, inputs) {
512
- const arg = test(scope, locals, assign, inputs)
513
- ? alternate(scope, locals, assign, inputs)
514
- : consequent(scope, locals, assign, inputs);
491
+ return function (scope, locals, assign) {
492
+ const arg = test(scope, locals, assign)
493
+ ? alternate(scope, locals, assign)
494
+ : consequent(scope, locals, assign);
515
495
  return context ? { value: arg } : arg;
516
496
  };
517
497
  }
@@ -532,7 +512,7 @@ export class ASTInterpreter {
532
512
  * Returns the value of an identifier.
533
513
  * @param {string} name - The identifier name.
534
514
  * @param {Object} [context] - The context.
535
- * @param {boolean} [create] - Whether to create the identifier if it does not exist.
515
+ * @param {boolean|1} [create] - Whether to create the identifier if it does not exist.
536
516
  * @returns {function} The function returning the identifier value.
537
517
  */
538
518
  identifier(name, context, create) {
@@ -554,16 +534,16 @@ export class ASTInterpreter {
554
534
  * @param {function} left - The left operand function.
555
535
  * @param {function} right - The right operand function.
556
536
  * @param {Object} [context] - The context.
557
- * @param {boolean} [create] - Whether to create the member if it does not exist.
537
+ * @param {boolean|1} [create] - Whether to create the member if it does not exist.
558
538
  * @returns {function} The function returning the computed member value.
559
539
  */
560
540
  computedMember(left, right, context, create) {
561
- return function (scope, locals, assign, inputs) {
562
- const lhs = left(scope, locals, assign, inputs);
541
+ return function (scope, locals, assign) {
542
+ const lhs = left(scope, locals, assign);
563
543
  let rhs;
564
544
  let value;
565
545
  if (lhs != null) {
566
- rhs = right(scope, locals, assign, inputs);
546
+ rhs = right(scope, locals, assign);
567
547
  rhs = getStringValue(rhs);
568
548
  if (create && create !== 1) {
569
549
  if (lhs && !lhs[rhs]) {
@@ -584,12 +564,12 @@ export class ASTInterpreter {
584
564
  * @param {function} left - The left operand function.
585
565
  * @param {string} right - The right operand function.
586
566
  * @param {Object} [context] - The context.
587
- * @param {boolean} [create] - Whether to create the member if it does not exist.
567
+ * @param {boolean|1} [create] - Whether to create the member if it does not exist.
588
568
  * @returns {function} The function returning the non-computed member value.
589
569
  */
590
570
  nonComputedMember(left, right, context, create) {
591
- return function (scope, locals, assign, inputs) {
592
- const lhs = left(scope, locals, assign, inputs);
571
+ return function (scope, locals, assign) {
572
+ const lhs = left(scope, locals, assign);
593
573
  if (create && create !== 1) {
594
574
  if (lhs && lhs[right] == null) {
595
575
  lhs[right] = {};
@@ -602,17 +582,306 @@ export class ASTInterpreter {
602
582
  return value;
603
583
  };
604
584
  }
585
+ }
605
586
 
606
- /**
607
- * Returns the value of an input expression.
608
- * @param {function} input - The input function.
609
- * @param {number} watchId - The watch identifier.
610
- * @returns {function} The function returning the input value.
611
- */
612
- inputs(input, watchId) {
613
- return function (scope, value, locals, inputs) {
614
- if (inputs) return inputs[watchId];
615
- return input(scope, value, locals);
587
+ /**
588
+ * @typedef {import("./ast").ASTNode & {
589
+ * isPure: boolean|number,
590
+ * constant: boolean,
591
+ * toWatch: Array,
592
+ * }} DecoratedASTNode
593
+ */
594
+
595
+ /**
596
+ * Decorates AST with constant, toWatch, and isPure properties
597
+ * @param {import("./ast").ASTNode} ast
598
+ * @param {function(any):any} $filter
599
+ * @param {boolean|1|2} [parentIsPure]
600
+ * @returns {DecoratedASTNode}
601
+ */
602
+ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
603
+ let allConstants;
604
+ let argsToWatch;
605
+ let isStatelessFilter;
606
+ let decoratedNode = /** @type {DecoratedASTNode} */ (ast);
607
+ let decoratedLeft,
608
+ decoratedRight,
609
+ decoratedTest,
610
+ decoratedAlternate,
611
+ decoratedConsequent,
612
+ decoratedObject,
613
+ decoratedProperty,
614
+ decoratedKey;
615
+ const astIsPure = (decoratedNode.isPure = isPure(ast, parentIsPure));
616
+
617
+ switch (ast.type) {
618
+ case ASTType.Program:
619
+ allConstants = true;
620
+ decoratedNode.body.forEach((expr) => {
621
+ let decorated = findConstantAndWatchExpressions(
622
+ expr.expression,
623
+ $filter,
624
+ astIsPure,
625
+ );
626
+ allConstants = allConstants && decorated.constant;
627
+ });
628
+ decoratedNode.constant = allConstants;
629
+ return decoratedNode;
630
+ case ASTType.Literal:
631
+ decoratedNode.constant = true;
632
+ decoratedNode.toWatch = [];
633
+ return decoratedNode;
634
+ case ASTType.UnaryExpression:
635
+ var decorated = findConstantAndWatchExpressions(
636
+ decoratedNode.argument,
637
+ $filter,
638
+ astIsPure,
639
+ );
640
+ decoratedNode.constant = decorated.constant;
641
+ decoratedNode.toWatch = decorated.toWatch;
642
+ return decoratedNode;
643
+ case ASTType.BinaryExpression:
644
+ decoratedLeft = findConstantAndWatchExpressions(
645
+ decoratedNode.left,
646
+ $filter,
647
+ astIsPure,
648
+ );
649
+ decoratedRight = findConstantAndWatchExpressions(
650
+ decoratedNode.right,
651
+ $filter,
652
+ astIsPure,
653
+ );
654
+ decoratedNode.constant =
655
+ decoratedLeft.constant && decoratedRight.constant;
656
+ decoratedNode.toWatch = decoratedLeft.toWatch.concat(
657
+ decoratedRight.toWatch,
658
+ );
659
+ return decoratedNode;
660
+ case ASTType.LogicalExpression:
661
+ decoratedLeft = findConstantAndWatchExpressions(
662
+ decoratedNode.left,
663
+ $filter,
664
+ astIsPure,
665
+ );
666
+ decoratedRight = findConstantAndWatchExpressions(
667
+ decoratedNode.right,
668
+ $filter,
669
+ astIsPure,
670
+ );
671
+ decoratedNode.constant =
672
+ decoratedLeft.constant && decoratedRight.constant;
673
+ decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
674
+ return decoratedNode;
675
+ case ASTType.ConditionalExpression:
676
+ decoratedTest = findConstantAndWatchExpressions(
677
+ ast.test,
678
+ $filter,
679
+ astIsPure,
680
+ );
681
+ decoratedAlternate = findConstantAndWatchExpressions(
682
+ ast.alternate,
683
+ $filter,
684
+ astIsPure,
685
+ );
686
+ decoratedConsequent = findConstantAndWatchExpressions(
687
+ ast.consequent,
688
+ $filter,
689
+ astIsPure,
690
+ );
691
+ decoratedNode.constant =
692
+ decoratedTest.constant &&
693
+ decoratedAlternate.constant &&
694
+ decoratedConsequent.constant;
695
+ decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
696
+ return decoratedNode;
697
+ case ASTType.Identifier:
698
+ decoratedNode.constant = false;
699
+ decoratedNode.toWatch = [ast];
700
+ return decoratedNode;
701
+ case ASTType.MemberExpression:
702
+ decoratedObject = findConstantAndWatchExpressions(
703
+ ast.object,
704
+ $filter,
705
+ astIsPure,
706
+ );
707
+ if (ast.computed) {
708
+ decoratedProperty = findConstantAndWatchExpressions(
709
+ ast.property,
710
+ $filter,
711
+ astIsPure,
712
+ );
713
+ }
714
+ decoratedNode.constant =
715
+ decoratedObject.constant &&
716
+ (!decoratedNode.computed || decoratedProperty.constant);
717
+ decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
718
+ return decoratedNode;
719
+ case ASTType.CallExpression:
720
+ isStatelessFilter = ast.filter
721
+ ? isStateless($filter, ast.callee.name)
722
+ : false;
723
+ allConstants = isStatelessFilter;
724
+ argsToWatch = [];
725
+ ast.arguments.forEach((expr) => {
726
+ decorated = findConstantAndWatchExpressions(expr, $filter, astIsPure);
727
+ allConstants = allConstants && decorated.constant;
728
+ argsToWatch.push.apply(argsToWatch, decorated.toWatch);
729
+ });
730
+ decoratedNode.constant = allConstants;
731
+ decoratedNode.toWatch = isStatelessFilter ? argsToWatch : [decoratedNode];
732
+ return decoratedNode;
733
+ case ASTType.AssignmentExpression:
734
+ decoratedLeft = findConstantAndWatchExpressions(
735
+ ast.left,
736
+ $filter,
737
+ astIsPure,
738
+ );
739
+ decoratedRight = findConstantAndWatchExpressions(
740
+ ast.right,
741
+ $filter,
742
+ astIsPure,
743
+ );
744
+ decoratedNode.constant =
745
+ decoratedLeft.constant && decoratedRight.constant;
746
+ decoratedNode.toWatch = [decoratedNode];
747
+ return decoratedNode;
748
+ case ASTType.ArrayExpression:
749
+ allConstants = true;
750
+ argsToWatch = [];
751
+ ast.elements.forEach((expr) => {
752
+ decorated = findConstantAndWatchExpressions(expr, $filter, astIsPure);
753
+ allConstants = allConstants && decorated.constant;
754
+ argsToWatch.push.apply(argsToWatch, decorated.toWatch);
755
+ });
756
+ decoratedNode.constant = allConstants;
757
+ decoratedNode.toWatch = argsToWatch;
758
+ return decoratedNode;
759
+ case ASTType.ObjectExpression:
760
+ allConstants = true;
761
+ argsToWatch = [];
762
+ ast.properties.forEach((property) => {
763
+ decorated = findConstantAndWatchExpressions(
764
+ property.value,
765
+ $filter,
766
+ astIsPure,
767
+ );
768
+ allConstants = allConstants && decorated.constant;
769
+ argsToWatch.push.apply(argsToWatch, decorated.toWatch);
770
+ if (property.computed) {
771
+ // `{[key]: value}` implicitly does `key.toString()` which may be non-pure
772
+ decoratedKey = findConstantAndWatchExpressions(
773
+ property.key,
774
+ $filter,
775
+ false,
776
+ );
777
+ allConstants = allConstants && decoratedKey.constant;
778
+ argsToWatch.push.apply(argsToWatch, decoratedKey.toWatch);
779
+ }
780
+ });
781
+ decoratedNode.constant = allConstants;
782
+ decoratedNode.toWatch = argsToWatch;
783
+ return decoratedNode;
784
+ case ASTType.ThisExpression:
785
+ decoratedNode.constant = false;
786
+ decoratedNode.toWatch = [];
787
+ return decoratedNode;
788
+ case ASTType.LocalsExpression:
789
+ decoratedNode.constant = false;
790
+ decoratedNode.toWatch = [];
791
+ return decoratedNode;
792
+ }
793
+ }
794
+
795
+ /**
796
+ * Converts a single expression AST node into an assignment expression if the expression is assignable.
797
+ *
798
+ * @param {import("./ast").ASTNode} ast
799
+ * @returns {import("./ast").ASTNode}
800
+ */
801
+ function assignableAST(ast) {
802
+ if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
803
+ return {
804
+ type: ASTType.AssignmentExpression,
805
+ left: ast.body[0].expression,
806
+ right: { type: ASTType.NGValueParameter },
807
+ operator: "=",
616
808
  };
617
809
  }
618
810
  }
811
+
812
+ function plusFn(l, r) {
813
+ if (typeof l === "undefined") return r;
814
+ if (typeof r === "undefined") return l;
815
+ return l + r;
816
+ }
817
+
818
+ /**
819
+ *
820
+ * @param {import("./ast").ASTNode[]} body
821
+ * @returns {any}
822
+ */
823
+ function getInputs(body) {
824
+ if (body.length !== 1) return;
825
+ const lastExpression = /** @type {DecoratedASTNode} */ (body[0].expression);
826
+ const candidate = lastExpression.toWatch;
827
+ if (candidate.length !== 1) return candidate;
828
+ return candidate[0] !== lastExpression ? candidate : undefined;
829
+ }
830
+
831
+ /**
832
+ * Detect nodes which could depend on non-shallow state of objects
833
+ * @param {import("./ast").ASTNode} node
834
+ * @param {boolean|PURITY_ABSOLUTE|PURITY_RELATIVE} parentIsPure
835
+ * @returns {boolean|PURITY_ABSOLUTE|PURITY_RELATIVE}
836
+ */
837
+ function isPure(node, parentIsPure) {
838
+ switch (node.type) {
839
+ // Computed members might invoke a stateful toString()
840
+ case ASTType.MemberExpression:
841
+ if (node.computed) {
842
+ return false;
843
+ }
844
+ break;
845
+
846
+ // Unary always convert to primitive
847
+ case ASTType.UnaryExpression:
848
+ return PURITY_ABSOLUTE;
849
+
850
+ // The binary + operator can invoke a stateful toString().
851
+ case ASTType.BinaryExpression:
852
+ return node.operator !== "+" ? PURITY_ABSOLUTE : false;
853
+
854
+ // Functions / filters probably read state from within objects
855
+ case ASTType.CallExpression:
856
+ return false;
857
+ }
858
+
859
+ return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
860
+ }
861
+
862
+ function isStateless($filter, filterName) {
863
+ const fn = $filter(filterName);
864
+ return !fn.$stateful;
865
+ }
866
+
867
+ /**
868
+ * Converts parameter to strings property name for use as keys in an object.
869
+ * Any non-string object, including a number, is typecasted into a string via the toString method.
870
+ * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names}
871
+ *
872
+ * @param {!any} name
873
+ * @returns {string}
874
+ */
875
+ function getStringValue(name) {
876
+ return `${name}`;
877
+ }
878
+
879
+ /**
880
+ * @param {import("./ast").ASTNode} ast
881
+ * @returns {boolean}
882
+ */
883
+ export function isAssignable(ast) {
884
+ return (
885
+ ast.type === ASTType.Identifier || ast.type === ASTType.MemberExpression
886
+ );
887
+ }