@angular-wave/angular.ts 0.0.47 → 0.0.49

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.
Files changed (64) hide show
  1. package/Makefile +3 -0
  2. package/README.md +1 -1
  3. package/css/angular.css +0 -6
  4. package/dist/angular-ts.esm.js +2 -2
  5. package/dist/angular-ts.umd.js +2 -2
  6. package/jsdoc.json +24 -0
  7. package/package.json +6 -2
  8. package/src/angular.spec.js +1 -2
  9. package/src/animations/animate-queue.js +0 -1
  10. package/src/animations/animation.js +1 -1
  11. package/src/animations/raf-scheduler.js +0 -1
  12. package/src/animations/shared.js +1 -1
  13. package/src/core/animate/animate.js +0 -1
  14. package/src/core/compile/compile.md +1 -1
  15. package/src/core/compile/compile.spec.js +49 -47
  16. package/src/core/location/location.spec.js +1 -1
  17. package/src/core/on.spec.js +7 -12
  18. package/src/core/parser/ast-type.js +22 -0
  19. package/src/core/parser/ast.js +426 -0
  20. package/src/core/parser/compiler.js +561 -0
  21. package/src/core/parser/interpreter.js +422 -0
  22. package/src/core/parser/lexer.js +345 -0
  23. package/src/core/parser/parse.js +23 -1984
  24. package/src/core/parser/parse.md +57 -0
  25. package/src/core/parser/parse.spec.js +2 -2
  26. package/src/core/parser/parser.js +45 -0
  27. package/src/core/parser/shared.js +228 -0
  28. package/src/core/prop.spec.js +4 -4
  29. package/src/core/q/q.spec.js +0 -1
  30. package/src/core/sce/sce.js +3 -6
  31. package/src/core/scope/scope.js +33 -21
  32. package/src/core/task-tracker-factory.js +0 -1
  33. package/src/directive/class/class.js +0 -2
  34. package/src/directive/form/form.js +0 -3
  35. package/src/directive/form/form.spec.js +18 -18
  36. package/src/directive/include/include.js +1 -1
  37. package/src/directive/include/include.spec.js +18 -19
  38. package/src/directive/input/input.js +1 -2
  39. package/src/directive/model/model.js +1 -3
  40. package/src/directive/model/model.spec.js +0 -1
  41. package/src/directive/repeat/repeat.spec.js +0 -2
  42. package/src/directive/switch/switch.spec.js +4 -4
  43. package/src/exts/aria/aria.js +0 -1
  44. package/src/filters/filter.spec.js +0 -1
  45. package/src/injector.js +1 -1
  46. package/src/injector.spec.js +0 -5
  47. package/src/loader.js +0 -5
  48. package/src/services/cookie-reader.js +0 -1
  49. package/src/services/http/http.spec.js +0 -2
  50. package/src/shared/constants.js +3 -2
  51. package/src/shared/utils.js +18 -7
  52. package/src/types.js +10 -0
  53. package/types/core/parser/ast-type.d.ts +20 -0
  54. package/types/core/parser/ast.d.ts +86 -0
  55. package/types/core/parser/compiler.d.ts +49 -0
  56. package/types/core/parser/interpreter.d.ts +57 -0
  57. package/types/core/parser/lexer.d.ts +153 -0
  58. package/types/core/parser/parse.d.ts +68 -0
  59. package/types/core/parser/parser.d.ts +28 -0
  60. package/types/core/parser/shared.d.ts +29 -0
  61. package/types/core/scope/scope.d.ts +19 -12
  62. package/types/shared/utils.d.ts +18 -5
  63. package/types/types.d.ts +1 -0
  64. package/types-back/index.d.ts +0 -12
@@ -4,1974 +4,17 @@ import {
4
4
  isDefined,
5
5
  isFunction,
6
6
  minErr,
7
- isString,
8
- isNumber,
9
7
  } from "../../shared/utils";
8
+ import { getValueOf, PURITY_RELATIVE } from "./shared";
9
+ import { Lexer } from "./lexer";
10
+ import { Parser } from "./parser";
10
11
 
11
- const $parseMinErr = minErr("$parse");
12
-
13
- const objectValueOf = {}.constructor.prototype.valueOf;
14
-
15
- /**
16
- * Converts parameter to strings property name for use as keys in an object.
17
- * Any non-string object, including a number, is typecasted into a string via the toString method.
18
- * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names}
19
- *
20
- * @param {!any} name
21
- * @returns {string}
22
- */
23
- function getStringValue(name) {
24
- return `${name}`;
25
- }
26
-
27
- const OPERATORS = Object.create(null);
28
-
29
- "+ - * / % === !== == != < > <= >= && || ! = |"
30
- .split(" ")
31
- .forEach((operator) => (OPERATORS[operator] = true));
32
-
33
- const ESCAPE = {
34
- n: "\n",
35
- f: "\f",
36
- r: "\r",
37
- t: "\t",
38
- v: "\v",
39
- "'": "'",
40
- '"': '"',
41
- };
42
-
43
- /// //////////////////////////////////////
44
-
45
- /**
46
- * @constructor
47
- */
48
- export const Lexer = function Lexer(options) {
49
- this.options = options;
50
- };
51
-
52
- Lexer.prototype = {
53
- constructor: Lexer,
54
-
55
- lex(text) {
56
- this.text = text;
57
- this.index = 0;
58
- this.tokens = [];
59
-
60
- while (this.index < this.text.length) {
61
- const ch = this.text.charAt(this.index);
62
- if (ch === '"' || ch === "'") {
63
- this.readString(ch);
64
- } else if (
65
- this.isNumber(ch) ||
66
- (ch === "." && this.isNumber(this.peek()))
67
- ) {
68
- this.readNumber();
69
- } else if (this.isIdentifierStart(this.peekMultichar())) {
70
- this.readIdent();
71
- } else if (this.is(ch, "(){}[].,;:?")) {
72
- this.tokens.push({ index: this.index, text: ch });
73
- this.index++;
74
- } else if (this.isWhitespace(ch)) {
75
- this.index++;
76
- } else {
77
- const ch2 = ch + this.peek();
78
- const ch3 = ch2 + this.peek(2);
79
- const op1 = OPERATORS[ch];
80
- const op2 = OPERATORS[ch2];
81
- const op3 = OPERATORS[ch3];
82
- if (op1 || op2 || op3) {
83
- const token = op3 ? ch3 : op2 ? ch2 : ch;
84
- this.tokens.push({ index: this.index, text: token, operator: true });
85
- this.index += token.length;
86
- } else {
87
- this.throwError(
88
- "Unexpected next character ",
89
- this.index,
90
- this.index + 1,
91
- );
92
- }
93
- }
94
- }
95
- return this.tokens;
96
- },
97
-
98
- is(ch, chars) {
99
- return chars.indexOf(ch) !== -1;
100
- },
101
-
102
- peek(i) {
103
- const num = i || 1;
104
- return this.index + num < this.text.length
105
- ? this.text.charAt(this.index + num)
106
- : false;
107
- },
108
-
109
- isNumber(ch) {
110
- return ch >= "0" && ch <= "9" && typeof ch === "string";
111
- },
112
-
113
- isWhitespace(ch) {
114
- // IE treats non-breaking space as \u00A0
115
- return (
116
- ch === " " ||
117
- ch === "\r" ||
118
- ch === "\t" ||
119
- ch === "\n" ||
120
- ch === "\v" ||
121
- ch === "\u00A0"
122
- );
123
- },
124
-
125
- isIdentifierStart(ch) {
126
- return this.options.isIdentifierStart
127
- ? this.options.isIdentifierStart(ch, this.codePointAt(ch))
128
- : this.isValidIdentifierStart(ch);
129
- },
130
-
131
- isValidIdentifierStart(ch) {
132
- return (
133
- (ch >= "a" && ch <= "z") ||
134
- (ch >= "A" && ch <= "Z") ||
135
- ch === "_" ||
136
- ch === "$"
137
- );
138
- },
139
-
140
- isIdentifierContinue(ch) {
141
- return this.options.isIdentifierContinue
142
- ? this.options.isIdentifierContinue(ch, this.codePointAt(ch))
143
- : this.isValidIdentifierContinue(ch);
144
- },
145
-
146
- isValidIdentifierContinue(ch) {
147
- return this.isValidIdentifierStart(ch) || this.isNumber(ch);
148
- },
149
-
150
- codePointAt(ch) {
151
- if (ch.length === 1) return ch.charCodeAt(0);
152
- // eslint-disable-next-line no-bitwise
153
- return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
154
- },
155
-
156
- peekMultichar() {
157
- const ch = this.text.charAt(this.index);
158
- const peek = this.peek();
159
- if (!peek) {
160
- return ch;
161
- }
162
- const cp1 = ch.charCodeAt(0);
163
- const cp2 = peek.charCodeAt(0);
164
- if (cp1 >= 0xd800 && cp1 <= 0xdbff && cp2 >= 0xdc00 && cp2 <= 0xdfff) {
165
- return ch + peek;
166
- }
167
- return ch;
168
- },
169
-
170
- isExpOperator(ch) {
171
- return ch === "-" || ch === "+" || this.isNumber(ch);
172
- },
173
-
174
- throwError(error, start, end) {
175
- end = end || this.index;
176
- const colStr = isDefined(start)
177
- ? `s ${start}-${this.index} [${this.text.substring(start, end)}]`
178
- : ` ${end}`;
179
- throw $parseMinErr(
180
- "lexerr",
181
- "Lexer Error: {0} at column{1} in expression [{2}].",
182
- error,
183
- colStr,
184
- this.text,
185
- );
186
- },
187
-
188
- readNumber() {
189
- let number = "";
190
- const start = this.index;
191
- while (this.index < this.text.length) {
192
- const ch = this.text.charAt(this.index).toLowerCase();
193
- if (ch === "." || this.isNumber(ch)) {
194
- number += ch;
195
- } else {
196
- const peekCh = this.peek();
197
- if (ch === "e" && this.isExpOperator(peekCh)) {
198
- number += ch;
199
- } else if (
200
- this.isExpOperator(ch) &&
201
- peekCh &&
202
- this.isNumber(peekCh) &&
203
- number.charAt(number.length - 1) === "e"
204
- ) {
205
- number += ch;
206
- } else if (
207
- this.isExpOperator(ch) &&
208
- (!peekCh || !this.isNumber(peekCh)) &&
209
- number.charAt(number.length - 1) === "e"
210
- ) {
211
- this.throwError("Invalid exponent");
212
- } else {
213
- break;
214
- }
215
- }
216
- this.index++;
217
- }
218
- this.tokens.push({
219
- index: start,
220
- text: number,
221
- constant: true,
222
- value: Number(number),
223
- });
224
- },
225
-
226
- readIdent() {
227
- const start = this.index;
228
- this.index += this.peekMultichar().length;
229
- while (this.index < this.text.length) {
230
- const ch = this.peekMultichar();
231
- if (!this.isIdentifierContinue(ch)) {
232
- break;
233
- }
234
- this.index += ch.length;
235
- }
236
- this.tokens.push({
237
- index: start,
238
- text: this.text.slice(start, this.index),
239
- identifier: true,
240
- });
241
- },
242
-
243
- readString(quote) {
244
- const start = this.index;
245
- this.index++;
246
- let string = "";
247
- let rawString = quote;
248
- let escape = false;
249
- while (this.index < this.text.length) {
250
- const ch = this.text.charAt(this.index);
251
- rawString += ch;
252
- if (escape) {
253
- if (ch === "u") {
254
- const hex = this.text.substring(this.index + 1, this.index + 5);
255
- if (!hex.match(/[\da-f]{4}/i)) {
256
- this.throwError(`Invalid unicode escape [\\u${hex}]`);
257
- }
258
- this.index += 4;
259
- string += String.fromCharCode(parseInt(hex, 16));
260
- } else {
261
- const rep = ESCAPE[ch];
262
- string += rep || ch;
263
- }
264
- escape = false;
265
- } else if (ch === "\\") {
266
- escape = true;
267
- } else if (ch === quote) {
268
- this.index++;
269
- this.tokens.push({
270
- index: start,
271
- text: rawString,
272
- constant: true,
273
- value: string,
274
- });
275
- return;
276
- } else {
277
- string += ch;
278
- }
279
- this.index++;
280
- }
281
- this.throwError("Unterminated quote", start);
282
- },
283
- };
284
-
285
- /**
286
- * @typedef {("Program"|"ExpressionStatement"|"AssignmentExpression"|"ConditionalExpression"|"LogicalExpression"|"BinaryExpression"|"UnaryExpression"|"CallExpression"|"MemberExpression"|"Identifier"|"Literal"|"ArrayExpression"|"Property"|"ObjectExpression"|"ThisExpression"|"LocalsExpression"|"NGValueParameter")} ASTType
287
- */
288
- const ASTType = {
289
- Program: "Program",
290
- ExpressionStatement: "ExpressionStatement",
291
- AssignmentExpression: "AssignmentExpression",
292
- ConditionalExpression: "ConditionalExpression",
293
- LogicalExpression: "LogicalExpression",
294
- BinaryExpression: "BinaryExpression",
295
- UnaryExpression: "UnaryExpression",
296
- CallExpression: "CallExpression",
297
- MemberExpression: "MemberExpression",
298
- Identifier: "Identifier",
299
- Literal: "Literal",
300
- ArrayExpression: "ArrayExpression",
301
- Property: "Property",
302
- ObjectExpression: "ObjectExpression",
303
- ThisExpression: "ThisExpression",
304
- LocalsExpression: "LocalsExpression",
305
- NGValueParameter: "NGValueParameter",
306
- };
307
-
308
- export function AST(lexer, options) {
309
- this.lexer = lexer;
310
- this.options = options;
311
- }
312
-
313
- AST.prototype = {
314
- ast(text) {
315
- this.text = text;
316
- this.tokens = this.lexer.lex(text);
317
-
318
- const value = this.program();
319
-
320
- if (this.tokens.length !== 0) {
321
- this.throwError("is an unexpected token", this.tokens[0]);
322
- }
323
-
324
- return value;
325
- },
326
-
327
- program() {
328
- const body = [];
329
- let hasMore = true;
330
- while (hasMore) {
331
- if (this.tokens.length > 0 && !this.peek("}", ")", ";", "]"))
332
- body.push(this.expressionStatement());
333
- if (!this.expect(";")) {
334
- hasMore = false;
335
- }
336
- }
337
- return { type: ASTType.Program, body };
338
- },
339
-
340
- expressionStatement() {
341
- return {
342
- type: ASTType.ExpressionStatement,
343
- expression: this.filterChain(),
344
- };
345
- },
346
-
347
- filterChain() {
348
- let left = this.expression();
349
- while (this.expect("|")) {
350
- left = this.filter(left);
351
- }
352
- return left;
353
- },
354
-
355
- expression() {
356
- return this.assignment();
357
- },
358
-
359
- assignment() {
360
- let result = this.ternary();
361
- if (this.expect("=")) {
362
- if (!isAssignable(result)) {
363
- throw $parseMinErr("lval", "Trying to assign a value to a non l-value");
364
- }
365
-
366
- result = {
367
- type: ASTType.AssignmentExpression,
368
- left: result,
369
- right: this.assignment(),
370
- operator: "=",
371
- };
372
- }
373
- return result;
374
- },
375
-
376
- ternary() {
377
- const test = this.logicalOR();
378
- let alternate;
379
- let consequent;
380
- if (this.expect("?")) {
381
- alternate = this.expression();
382
- if (this.consume(":")) {
383
- consequent = this.expression();
384
- return {
385
- type: ASTType.ConditionalExpression,
386
- test,
387
- alternate,
388
- consequent,
389
- };
390
- }
391
- }
392
- return test;
393
- },
394
-
395
- logicalOR() {
396
- let left = this.logicalAND();
397
- while (this.expect("||")) {
398
- left = {
399
- type: ASTType.LogicalExpression,
400
- operator: "||",
401
- left,
402
- right: this.logicalAND(),
403
- };
404
- }
405
- return left;
406
- },
407
-
408
- logicalAND() {
409
- let left = this.equality();
410
- while (this.expect("&&")) {
411
- left = {
412
- type: ASTType.LogicalExpression,
413
- operator: "&&",
414
- left,
415
- right: this.equality(),
416
- };
417
- }
418
- return left;
419
- },
420
-
421
- equality() {
422
- let left = this.relational();
423
- let token;
424
- while ((token = this.expect("==", "!=", "===", "!=="))) {
425
- left = {
426
- type: ASTType.BinaryExpression,
427
- operator: token.text,
428
- left,
429
- right: this.relational(),
430
- };
431
- }
432
- return left;
433
- },
434
-
435
- relational() {
436
- let left = this.additive();
437
- let token;
438
- while ((token = this.expect("<", ">", "<=", ">="))) {
439
- left = {
440
- type: ASTType.BinaryExpression,
441
- operator: token.text,
442
- left,
443
- right: this.additive(),
444
- };
445
- }
446
- return left;
447
- },
448
-
449
- additive() {
450
- let left = this.multiplicative();
451
- let token;
452
- while ((token = this.expect("+", "-"))) {
453
- left = {
454
- type: ASTType.BinaryExpression,
455
- operator: token.text,
456
- left,
457
- right: this.multiplicative(),
458
- };
459
- }
460
- return left;
461
- },
462
-
463
- multiplicative() {
464
- let left = this.unary();
465
- let token;
466
- while ((token = this.expect("*", "/", "%"))) {
467
- left = {
468
- type: ASTType.BinaryExpression,
469
- operator: token.text,
470
- left,
471
- right: this.unary(),
472
- };
473
- }
474
- return left;
475
- },
476
-
477
- unary() {
478
- let token;
479
- if ((token = this.expect("+", "-", "!"))) {
480
- return {
481
- type: ASTType.UnaryExpression,
482
- operator: token.text,
483
- prefix: true,
484
- argument: this.unary(),
485
- };
486
- }
487
- return this.primary();
488
- },
489
-
490
- primary() {
491
- let primary;
492
- if (this.expect("(")) {
493
- primary = this.filterChain();
494
- this.consume(")");
495
- } else if (this.expect("[")) {
496
- primary = this.arrayDeclaration();
497
- } else if (this.expect("{")) {
498
- primary = this.object();
499
- } else if (
500
- Object.prototype.hasOwnProperty.call(
501
- this.selfReferential,
502
- this.peek().text,
503
- )
504
- ) {
505
- primary = structuredClone(this.selfReferential[this.consume().text]);
506
- } else if (
507
- Object.prototype.hasOwnProperty.call(
508
- this.options.literals,
509
- this.peek().text,
510
- )
511
- ) {
512
- primary = {
513
- type: ASTType.Literal,
514
- value: this.options.literals[this.consume().text],
515
- };
516
- } else if (this.peek().identifier) {
517
- primary = this.identifier();
518
- } else if (this.peek().constant) {
519
- primary = this.constant();
520
- } else {
521
- this.throwError("not a primary expression", this.peek());
522
- }
523
-
524
- let next;
525
- while ((next = this.expect("(", "[", "."))) {
526
- if (next.text === "(") {
527
- primary = {
528
- type: ASTType.CallExpression,
529
- callee: primary,
530
- arguments: this.parseArguments(),
531
- };
532
- this.consume(")");
533
- } else if (next.text === "[") {
534
- primary = {
535
- type: ASTType.MemberExpression,
536
- object: primary,
537
- property: this.expression(),
538
- computed: true,
539
- };
540
- this.consume("]");
541
- } else if (next.text === ".") {
542
- primary = {
543
- type: ASTType.MemberExpression,
544
- object: primary,
545
- property: this.identifier(),
546
- computed: false,
547
- };
548
- } else {
549
- this.throwError("IMPOSSIBLE");
550
- }
551
- }
552
- return primary;
553
- },
554
-
555
- filter(baseExpression) {
556
- const args = [baseExpression];
557
- const result = {
558
- type: ASTType.CallExpression,
559
- callee: this.identifier(),
560
- arguments: args,
561
- filter: true,
562
- };
563
-
564
- while (this.expect(":")) {
565
- args.push(this.expression());
566
- }
567
-
568
- return result;
569
- },
570
-
571
- parseArguments() {
572
- const args = [];
573
- if (this.peekToken().text !== ")") {
574
- do {
575
- args.push(this.filterChain());
576
- } while (this.expect(","));
577
- }
578
- return args;
579
- },
580
-
581
- identifier() {
582
- const token = this.consume();
583
- if (!token.identifier) {
584
- this.throwError("is not a valid identifier", token);
585
- }
586
- return { type: ASTType.Identifier, name: token.text };
587
- },
588
-
589
- constant() {
590
- // TODO check that it is a constant
591
- return { type: ASTType.Literal, value: this.consume().value };
592
- },
593
-
594
- arrayDeclaration() {
595
- const elements = [];
596
- if (this.peekToken().text !== "]") {
597
- do {
598
- if (this.peek("]")) {
599
- // Support trailing commas per ES5.1.
600
- break;
601
- }
602
- elements.push(this.expression());
603
- } while (this.expect(","));
604
- }
605
- this.consume("]");
606
-
607
- return { type: ASTType.ArrayExpression, elements };
608
- },
609
-
610
- object() {
611
- const properties = [];
612
- let property;
613
- if (this.peekToken().text !== "}") {
614
- do {
615
- if (this.peek("}")) {
616
- // Support trailing commas per ES5.1.
617
- break;
618
- }
619
- property = { type: ASTType.Property, kind: "init" };
620
- if (this.peek().constant) {
621
- property.key = this.constant();
622
- property.computed = false;
623
- this.consume(":");
624
- property.value = this.expression();
625
- } else if (this.peek().identifier) {
626
- property.key = this.identifier();
627
- property.computed = false;
628
- if (this.peek(":")) {
629
- this.consume(":");
630
- property.value = this.expression();
631
- } else {
632
- property.value = property.key;
633
- }
634
- } else if (this.peek("[")) {
635
- this.consume("[");
636
- property.key = this.expression();
637
- this.consume("]");
638
- property.computed = true;
639
- this.consume(":");
640
- property.value = this.expression();
641
- } else {
642
- this.throwError("invalid key", this.peek());
643
- }
644
- properties.push(property);
645
- } while (this.expect(","));
646
- }
647
- this.consume("}");
648
-
649
- return { type: ASTType.ObjectExpression, properties };
650
- },
651
-
652
- throwError(msg, token) {
653
- throw $parseMinErr(
654
- "syntax",
655
- "Syntax Error: Token '{0}' {1} at column {2} of the expression [{3}] starting at [{4}].",
656
- token.text,
657
- msg,
658
- token.index + 1,
659
- this.text,
660
- this.text.substring(token.index),
661
- );
662
- },
663
-
664
- consume(e1) {
665
- if (this.tokens.length === 0) {
666
- throw $parseMinErr(
667
- "ueoe",
668
- "Unexpected end of expression: {0}",
669
- this.text,
670
- );
671
- }
672
-
673
- const token = this.expect(e1);
674
- if (!token) {
675
- this.throwError(`is unexpected, expecting [${e1}]`, this.peek());
676
- }
677
- return token;
678
- },
679
-
680
- peekToken() {
681
- if (this.tokens.length === 0) {
682
- throw $parseMinErr(
683
- "ueoe",
684
- "Unexpected end of expression: {0}",
685
- this.text,
686
- );
687
- }
688
- return this.tokens[0];
689
- },
690
-
691
- peek(e1, e2, e3, e4) {
692
- return this.peekAhead(0, e1, e2, e3, e4);
693
- },
694
-
695
- peekAhead(i, e1, e2, e3, e4) {
696
- if (this.tokens.length > i) {
697
- const token = this.tokens[i];
698
- const t = token.text;
699
- if (
700
- t === e1 ||
701
- t === e2 ||
702
- t === e3 ||
703
- t === e4 ||
704
- (!e1 && !e2 && !e3 && !e4)
705
- ) {
706
- return token;
707
- }
708
- }
709
- return false;
710
- },
711
-
712
- expect(e1, e2, e3, e4) {
713
- const token = this.peek(e1, e2, e3, e4);
714
- if (token) {
715
- this.tokens.shift();
716
- return token;
717
- }
718
- return false;
719
- },
720
-
721
- selfReferential: {
722
- this: { type: ASTType.ThisExpression },
723
- $locals: { type: ASTType.LocalsExpression },
724
- },
725
- };
726
-
727
- function ifDefined(v, d) {
728
- return typeof v !== "undefined" ? v : d;
729
- }
730
-
731
- function plusFn(l, r) {
732
- if (typeof l === "undefined") return r;
733
- if (typeof r === "undefined") return l;
734
- return l + r;
735
- }
736
-
737
- function isStateless($filter, filterName) {
738
- const fn = $filter(filterName);
739
- return !fn.$stateful;
740
- }
741
-
742
- const PURITY_ABSOLUTE = 1;
743
- const PURITY_RELATIVE = 2;
744
-
745
- // Detect nodes which could depend on non-shallow state of objects
746
- function isPure(node, parentIsPure) {
747
- switch (node.type) {
748
- // Computed members might invoke a stateful toString()
749
- case ASTType.MemberExpression:
750
- if (node.computed) {
751
- return false;
752
- }
753
- break;
754
-
755
- // Unary always convert to primative
756
- case ASTType.UnaryExpression:
757
- return PURITY_ABSOLUTE;
758
-
759
- // The binary + operator can invoke a stateful toString().
760
- case ASTType.BinaryExpression:
761
- return node.operator !== "+" ? PURITY_ABSOLUTE : false;
762
-
763
- // Functions / filters probably read state from within objects
764
- case ASTType.CallExpression:
765
- return false;
766
- }
767
-
768
- return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
769
- }
770
-
771
- function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
772
- let allConstants;
773
- let argsToWatch;
774
- let isStatelessFilter;
775
-
776
- const astIsPure = (ast.isPure = isPure(ast, parentIsPure));
777
-
778
- switch (ast.type) {
779
- case ASTType.Program:
780
- allConstants = true;
781
- /** @type {[any]} */ (ast.body).forEach((expr) => {
782
- findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
783
- allConstants = allConstants && expr.expression.constant;
784
- });
785
- ast.constant = allConstants;
786
- break;
787
- case ASTType.Literal:
788
- ast.constant = true;
789
- ast.toWatch = [];
790
- break;
791
- case ASTType.UnaryExpression:
792
- findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
793
- ast.constant = ast.argument.constant;
794
- ast.toWatch = ast.argument.toWatch;
795
- break;
796
- case ASTType.BinaryExpression:
797
- findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
798
- findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
799
- ast.constant = ast.left.constant && ast.right.constant;
800
- ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
801
- break;
802
- case ASTType.LogicalExpression:
803
- findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
804
- findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
805
- ast.constant = ast.left.constant && ast.right.constant;
806
- ast.toWatch = ast.constant ? [] : [ast];
807
- break;
808
- case ASTType.ConditionalExpression:
809
- findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
810
- findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
811
- findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
812
- ast.constant =
813
- ast.test.constant && ast.alternate.constant && ast.consequent.constant;
814
- ast.toWatch = ast.constant ? [] : [ast];
815
- break;
816
- case ASTType.Identifier:
817
- ast.constant = false;
818
- ast.toWatch = [ast];
819
- break;
820
- case ASTType.MemberExpression:
821
- findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
822
- if (ast.computed) {
823
- findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
824
- }
825
- ast.constant =
826
- ast.object.constant && (!ast.computed || ast.property.constant);
827
- ast.toWatch = ast.constant ? [] : [ast];
828
- break;
829
- case ASTType.CallExpression:
830
- isStatelessFilter = ast.filter
831
- ? isStateless($filter, ast.callee.name)
832
- : false;
833
- allConstants = isStatelessFilter;
834
- argsToWatch = [];
835
- forEach(ast.arguments, (expr) => {
836
- findConstantAndWatchExpressions(expr, $filter, astIsPure);
837
- allConstants = allConstants && expr.constant;
838
- argsToWatch.push.apply(argsToWatch, expr.toWatch);
839
- });
840
- ast.constant = allConstants;
841
- ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
842
- break;
843
- case ASTType.AssignmentExpression:
844
- findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
845
- findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
846
- ast.constant = ast.left.constant && ast.right.constant;
847
- ast.toWatch = [ast];
848
- break;
849
- case ASTType.ArrayExpression:
850
- allConstants = true;
851
- argsToWatch = [];
852
- forEach(ast.elements, (expr) => {
853
- findConstantAndWatchExpressions(expr, $filter, astIsPure);
854
- allConstants = allConstants && expr.constant;
855
- argsToWatch.push.apply(argsToWatch, expr.toWatch);
856
- });
857
- ast.constant = allConstants;
858
- ast.toWatch = argsToWatch;
859
- break;
860
- case ASTType.ObjectExpression:
861
- allConstants = true;
862
- argsToWatch = [];
863
- forEach(ast.properties, (property) => {
864
- findConstantAndWatchExpressions(property.value, $filter, astIsPure);
865
- allConstants = allConstants && property.value.constant;
866
- argsToWatch.push.apply(argsToWatch, property.value.toWatch);
867
- if (property.computed) {
868
- // `{[key]: value}` implicitly does `key.toString()` which may be non-pure
869
- findConstantAndWatchExpressions(
870
- property.key,
871
- $filter,
872
- /* parentIsPure= */ false,
873
- );
874
- allConstants = allConstants && property.key.constant;
875
- argsToWatch.push.apply(argsToWatch, property.key.toWatch);
876
- }
877
- });
878
- ast.constant = allConstants;
879
- ast.toWatch = argsToWatch;
880
- break;
881
- case ASTType.ThisExpression:
882
- ast.constant = false;
883
- ast.toWatch = [];
884
- break;
885
- case ASTType.LocalsExpression:
886
- ast.constant = false;
887
- ast.toWatch = [];
888
- break;
889
- }
890
- }
891
-
892
- function getInputs(body) {
893
- if (body.length !== 1) return;
894
- const lastExpression = body[0].expression;
895
- const candidate = lastExpression.toWatch;
896
- if (candidate.length !== 1) return candidate;
897
- return candidate[0] !== lastExpression ? candidate : undefined;
898
- }
899
-
900
- function isAssignable(ast) {
901
- return (
902
- ast.type === ASTType.Identifier || ast.type === ASTType.MemberExpression
903
- );
904
- }
905
-
906
- function assignableAST(ast) {
907
- if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
908
- return {
909
- type: ASTType.AssignmentExpression,
910
- left: ast.body[0].expression,
911
- right: { type: ASTType.NGValueParameter },
912
- operator: "=",
913
- };
914
- }
915
- }
916
-
917
- function isLiteral(ast) {
918
- return (
919
- ast.body.length === 0 ||
920
- (ast.body.length === 1 &&
921
- (ast.body[0].expression.type === ASTType.Literal ||
922
- ast.body[0].expression.type === ASTType.ArrayExpression ||
923
- ast.body[0].expression.type === ASTType.ObjectExpression))
924
- );
925
- }
926
-
927
- function isConstant(ast) {
928
- return ast.constant;
929
- }
930
-
931
- function ASTCompiler($filter) {
932
- this.$filter = $filter;
933
- }
934
-
935
- ASTCompiler.prototype = {
936
- compile(ast) {
937
- const self = this;
938
- this.state = {
939
- nextId: 0,
940
- filters: {},
941
- fn: { vars: [], body: [], own: {} },
942
- assign: { vars: [], body: [], own: {} },
943
- inputs: [],
944
- };
945
- findConstantAndWatchExpressions(ast, self.$filter);
946
- let extra = "";
947
- let assignable;
948
- this.stage = "assign";
949
- if ((assignable = assignableAST(ast))) {
950
- this.state.computing = "assign";
951
- const result = this.nextId();
952
- this.recurse(assignable, result);
953
- this.return_(result);
954
- extra = `fn.assign=${this.generateFunction("assign", "s,v,l")}`;
955
- }
956
- const toWatch = getInputs(ast.body);
957
- self.stage = "inputs";
958
- forEach(toWatch, (watch, key) => {
959
- const fnKey = `fn${key}`;
960
- self.state[fnKey] = { vars: [], body: [], own: {} };
961
- self.state.computing = fnKey;
962
- const intoId = self.nextId();
963
- self.recurse(watch, intoId);
964
- self.return_(intoId);
965
- self.state.inputs.push({ name: fnKey, isPure: watch.isPure });
966
- watch.watchId = key;
967
- });
968
- this.state.computing = "fn";
969
- this.stage = "main";
970
- this.recurse(ast);
971
- const fnString = `\n${this.filterPrefix()}let fn=${this.generateFunction(
972
- "fn",
973
- "s,l,a,i",
974
- )}${extra}${this.watchFns()}return fn;`;
975
-
976
- // eslint-disable-next-line no-new-func
977
- const fn = new Function(
978
- "$filter",
979
- "getStringValue",
980
- "ifDefined",
981
- "plus",
982
- fnString,
983
- )(this.$filter, getStringValue, ifDefined, plusFn);
984
- this.state = this.stage = undefined;
985
- return fn;
986
- },
987
-
988
- watchFns() {
989
- const result = [];
990
- const { inputs } = this.state;
991
- const self = this;
992
- forEach(inputs, (input) => {
993
- result.push(
994
- `let ${input.name}=${self.generateFunction(input.name, "s")}`,
995
- );
996
- if (input.isPure) {
997
- result.push(input.name, `.isPure=${JSON.stringify(input.isPure)};`);
998
- }
999
- });
1000
- if (inputs.length) {
1001
- result.push(`fn.inputs=[${inputs.map((i) => i.name).join(",")}];`);
1002
- }
1003
- return result.join("");
1004
- },
1005
-
1006
- generateFunction(name, params) {
1007
- return `function(${params}){${this.varsPrefix(name)}${this.body(name)}};`;
1008
- },
1009
-
1010
- filterPrefix() {
1011
- const parts = [];
1012
- const self = this;
1013
- forEach(this.state.filters, (id, filter) => {
1014
- parts.push(`${id}=$filter(${self.escape(filter)})`);
1015
- });
1016
- if (parts.length) return `let ${parts.join(",")};`;
1017
- return "";
1018
- },
1019
-
1020
- varsPrefix(section) {
1021
- return this.state[section].vars.length
1022
- ? `let ${this.state[section].vars.join(",")};`
1023
- : "";
1024
- },
1025
-
1026
- body(section) {
1027
- return this.state[section].body.join("");
1028
- },
1029
-
1030
- recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
1031
- let left;
1032
- let right;
1033
- const self = this;
1034
- let args;
1035
- let expression;
1036
- let computed;
1037
- recursionFn = recursionFn || (() => {});
1038
- if (!skipWatchIdCheck && isDefined(ast.watchId)) {
1039
- intoId = intoId || this.nextId();
1040
- this.if_(
1041
- "i",
1042
- this.lazyAssign(intoId, this.computedMember("i", ast.watchId)),
1043
- this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true),
1044
- );
1045
- return;
1046
- }
1047
- switch (ast.type) {
1048
- case ASTType.Program:
1049
- forEach(ast.body, (expression, pos) => {
1050
- self.recurse(expression.expression, undefined, undefined, (expr) => {
1051
- right = expr;
1052
- });
1053
- if (pos !== ast.body.length - 1) {
1054
- self.current().body.push(right, ";");
1055
- } else {
1056
- self.return_(right);
1057
- }
1058
- });
1059
- break;
1060
- case ASTType.Literal:
1061
- expression = this.escape(ast.value);
1062
- this.assign(intoId, expression);
1063
- recursionFn(intoId || expression);
1064
- break;
1065
- case ASTType.UnaryExpression:
1066
- this.recurse(ast.argument, undefined, undefined, (expr) => {
1067
- right = expr;
1068
- });
1069
- expression = `${ast.operator}(${this.ifDefined(right, 0)})`;
1070
- this.assign(intoId, expression);
1071
- recursionFn(expression);
1072
- break;
1073
- case ASTType.BinaryExpression:
1074
- this.recurse(ast.left, undefined, undefined, (expr) => {
1075
- left = expr;
1076
- });
1077
- this.recurse(ast.right, undefined, undefined, (expr) => {
1078
- right = expr;
1079
- });
1080
- if (ast.operator === "+") {
1081
- expression = this.plus(left, right);
1082
- } else if (ast.operator === "-") {
1083
- expression =
1084
- this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
1085
- } else {
1086
- expression = `(${left})${ast.operator}(${right})`;
1087
- }
1088
- this.assign(intoId, expression);
1089
- recursionFn(expression);
1090
- break;
1091
- case ASTType.LogicalExpression:
1092
- intoId = intoId || this.nextId();
1093
- self.recurse(ast.left, intoId);
1094
- self.if_(
1095
- ast.operator === "&&" ? intoId : self.not(intoId),
1096
- self.lazyRecurse(ast.right, intoId),
1097
- );
1098
- recursionFn(intoId);
1099
- break;
1100
- case ASTType.ConditionalExpression:
1101
- intoId = intoId || this.nextId();
1102
- self.recurse(ast.test, intoId);
1103
- self.if_(
1104
- intoId,
1105
- self.lazyRecurse(ast.alternate, intoId),
1106
- self.lazyRecurse(ast.consequent, intoId),
1107
- );
1108
- recursionFn(intoId);
1109
- break;
1110
- case ASTType.Identifier:
1111
- intoId = intoId || this.nextId();
1112
- if (nameId) {
1113
- nameId.context =
1114
- self.stage === "inputs"
1115
- ? "s"
1116
- : this.assign(
1117
- this.nextId(),
1118
- `${this.getHasOwnProperty("l", ast.name)}?l:s`,
1119
- );
1120
- nameId.computed = false;
1121
- nameId.name = ast.name;
1122
- }
1123
- self.if_(
1124
- self.stage === "inputs" ||
1125
- self.not(self.getHasOwnProperty("l", ast.name)),
1126
- () => {
1127
- self.if_(self.stage === "inputs" || "s", () => {
1128
- if (create && create !== 1) {
1129
- self.if_(
1130
- self.isNull(self.nonComputedMember("s", ast.name)),
1131
- self.lazyAssign(self.nonComputedMember("s", ast.name), "{}"),
1132
- );
1133
- }
1134
- self.assign(intoId, self.nonComputedMember("s", ast.name));
1135
- });
1136
- },
1137
- intoId &&
1138
- self.lazyAssign(intoId, self.nonComputedMember("l", ast.name)),
1139
- );
1140
- recursionFn(intoId);
1141
- break;
1142
- case ASTType.MemberExpression:
1143
- left = (nameId && (nameId.context = this.nextId())) || this.nextId();
1144
- intoId = intoId || this.nextId();
1145
- self.recurse(
1146
- ast.object,
1147
- left,
1148
- undefined,
1149
- () => {
1150
- self.if_(
1151
- self.notNull(left),
1152
- () => {
1153
- if (ast.computed) {
1154
- right = self.nextId();
1155
- self.recurse(ast.property, right);
1156
- self.getStringValue(right);
1157
- if (create && create !== 1) {
1158
- self.if_(
1159
- self.not(self.computedMember(left, right)),
1160
- self.lazyAssign(self.computedMember(left, right), "{}"),
1161
- );
1162
- }
1163
- expression = self.computedMember(left, right);
1164
- self.assign(intoId, expression);
1165
- if (nameId) {
1166
- nameId.computed = true;
1167
- nameId.name = right;
1168
- }
1169
- } else {
1170
- if (create && create !== 1) {
1171
- self.if_(
1172
- self.isNull(
1173
- self.nonComputedMember(left, ast.property.name),
1174
- ),
1175
- self.lazyAssign(
1176
- self.nonComputedMember(left, ast.property.name),
1177
- "{}",
1178
- ),
1179
- );
1180
- }
1181
- expression = self.nonComputedMember(left, ast.property.name);
1182
- self.assign(intoId, expression);
1183
- if (nameId) {
1184
- nameId.computed = false;
1185
- nameId.name = ast.property.name;
1186
- }
1187
- }
1188
- },
1189
- () => {
1190
- self.assign(intoId, "undefined");
1191
- },
1192
- );
1193
- recursionFn(intoId);
1194
- },
1195
- !!create,
1196
- );
1197
- break;
1198
- case ASTType.CallExpression:
1199
- intoId = intoId || this.nextId();
1200
- if (ast.filter) {
1201
- right = self.filter(ast.callee.name);
1202
- args = [];
1203
- forEach(ast.arguments, (expr) => {
1204
- const argument = self.nextId();
1205
- self.recurse(expr, argument);
1206
- args.push(argument);
1207
- });
1208
- expression = `${right}(${args.join(",")})`;
1209
- self.assign(intoId, expression);
1210
- recursionFn(intoId);
1211
- } else {
1212
- right = self.nextId();
1213
- left = {};
1214
- args = [];
1215
- self.recurse(ast.callee, right, left, () => {
1216
- self.if_(
1217
- self.notNull(right),
1218
- () => {
1219
- forEach(ast.arguments, (expr) => {
1220
- self.recurse(
1221
- expr,
1222
- ast.constant ? undefined : self.nextId(),
1223
- undefined,
1224
- (argument) => {
1225
- args.push(argument);
1226
- },
1227
- );
1228
- });
1229
- if (left.name) {
1230
- expression = `${self.member(
1231
- left.context,
1232
- left.name,
1233
- left.computed,
1234
- )}(${args.join(",")})`;
1235
- } else {
1236
- expression = `${right}(${args.join(",")})`;
1237
- }
1238
- self.assign(intoId, expression);
1239
- },
1240
- () => {
1241
- self.assign(intoId, "undefined");
1242
- },
1243
- );
1244
- recursionFn(intoId);
1245
- });
1246
- }
1247
- break;
1248
- case ASTType.AssignmentExpression:
1249
- right = this.nextId();
1250
- left = {};
1251
- this.recurse(
1252
- ast.left,
1253
- undefined,
1254
- left,
1255
- () => {
1256
- self.if_(self.notNull(left.context), () => {
1257
- self.recurse(ast.right, right);
1258
- expression =
1259
- self.member(left.context, left.name, left.computed) +
1260
- ast.operator +
1261
- right;
1262
- self.assign(intoId, expression);
1263
- recursionFn(intoId || expression);
1264
- });
1265
- },
1266
- 1,
1267
- );
1268
- break;
1269
- case ASTType.ArrayExpression:
1270
- args = [];
1271
- forEach(ast.elements, (expr) => {
1272
- self.recurse(
1273
- expr,
1274
- ast.constant ? undefined : self.nextId(),
1275
- undefined,
1276
- (argument) => {
1277
- args.push(argument);
1278
- },
1279
- );
1280
- });
1281
- expression = `[${args.join(",")}]`;
1282
- this.assign(intoId, expression);
1283
- recursionFn(intoId || expression);
1284
- break;
1285
- case ASTType.ObjectExpression:
1286
- args = [];
1287
- computed = false;
1288
- forEach(ast.properties, (property) => {
1289
- if (property.computed) {
1290
- computed = true;
1291
- }
1292
- });
1293
- if (computed) {
1294
- intoId = intoId || this.nextId();
1295
- this.assign(intoId, "{}");
1296
- forEach(ast.properties, (property) => {
1297
- if (property.computed) {
1298
- left = self.nextId();
1299
- self.recurse(property.key, left);
1300
- } else {
1301
- left =
1302
- property.key.type === ASTType.Identifier
1303
- ? property.key.name
1304
- : `${property.key.value}`;
1305
- }
1306
- right = self.nextId();
1307
- self.recurse(property.value, right);
1308
- self.assign(self.member(intoId, left, property.computed), right);
1309
- });
1310
- } else {
1311
- forEach(ast.properties, (property) => {
1312
- self.recurse(
1313
- property.value,
1314
- ast.constant ? undefined : self.nextId(),
1315
- undefined,
1316
- (expr) => {
1317
- args.push(
1318
- `${self.escape(
1319
- property.key.type === ASTType.Identifier
1320
- ? property.key.name
1321
- : `${property.key.value}`,
1322
- )}:${expr}`,
1323
- );
1324
- },
1325
- );
1326
- });
1327
- expression = `{${args.join(",")}}`;
1328
- this.assign(intoId, expression);
1329
- }
1330
- recursionFn(intoId || expression);
1331
- break;
1332
- case ASTType.ThisExpression:
1333
- this.assign(intoId, "s");
1334
- recursionFn(intoId || "s");
1335
- break;
1336
- case ASTType.LocalsExpression:
1337
- this.assign(intoId, "l");
1338
- recursionFn(intoId || "l");
1339
- break;
1340
- case ASTType.NGValueParameter:
1341
- this.assign(intoId, "v");
1342
- recursionFn(intoId || "v");
1343
- break;
1344
- }
1345
- },
1346
-
1347
- getHasOwnProperty(element, property) {
1348
- const key = `${element}.${property}`;
1349
- const { own } = this.current();
1350
- if (!Object.prototype.hasOwnProperty.call(own, key)) {
1351
- own[key] = this.nextId(
1352
- false,
1353
- `${element}&&(${this.escape(property)} in ${element})`,
1354
- );
1355
- }
1356
- return own[key];
1357
- },
1358
-
1359
- assign(id, value) {
1360
- if (!id) return;
1361
- this.current().body.push(id, "=", value, ";");
1362
- return id;
1363
- },
1364
-
1365
- filter(filterName) {
1366
- if (!Object.prototype.hasOwnProperty.call(this.state.filters, filterName)) {
1367
- this.state.filters[filterName] = this.nextId(true);
1368
- }
1369
- return this.state.filters[filterName];
1370
- },
1371
-
1372
- ifDefined(id, defaultValue) {
1373
- return `ifDefined(${id},${this.escape(defaultValue)})`;
1374
- },
1375
-
1376
- plus(left, right) {
1377
- return `plus(${left},${right})`;
1378
- },
1379
-
1380
- return_(id) {
1381
- this.current().body.push("return ", id, ";");
1382
- },
1383
-
1384
- if_(test, alternate, consequent) {
1385
- if (test === true) {
1386
- alternate();
1387
- } else {
1388
- const { body } = this.current();
1389
- body.push("if(", test, "){");
1390
- alternate();
1391
- body.push("}");
1392
- if (consequent) {
1393
- body.push("else{");
1394
- consequent();
1395
- body.push("}");
1396
- }
1397
- }
1398
- },
1399
-
1400
- not(expression) {
1401
- return `!(${expression})`;
1402
- },
1403
-
1404
- isNull(expression) {
1405
- return `${expression}==null`;
1406
- },
1407
-
1408
- notNull(expression) {
1409
- return `${expression}!=null`;
1410
- },
1411
-
1412
- nonComputedMember(left, right) {
1413
- const SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
1414
- const UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
1415
- if (SAFE_IDENTIFIER.test(right)) {
1416
- return `${left}.${right}`;
1417
- }
1418
- return `${left}["${right.replace(
1419
- UNSAFE_CHARACTERS,
1420
- this.stringEscapeFn,
1421
- )}"]`;
1422
- },
1423
-
1424
- computedMember(left, right) {
1425
- return `${left}[${right}]`;
1426
- },
1427
-
1428
- member(left, right, computed) {
1429
- if (computed) return this.computedMember(left, right);
1430
- return this.nonComputedMember(left, right);
1431
- },
1432
-
1433
- getStringValue(item) {
1434
- this.assign(item, `getStringValue(${item})`);
1435
- },
1436
-
1437
- lazyRecurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
1438
- const self = this;
1439
- return function () {
1440
- self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
1441
- };
1442
- },
1443
-
1444
- lazyAssign(id, value) {
1445
- const self = this;
1446
- return function () {
1447
- self.assign(id, value);
1448
- };
1449
- },
1450
-
1451
- stringEscapeRegex: /[^ a-zA-Z0-9]/g,
1452
-
1453
- stringEscapeFn(c) {
1454
- return `\\u${`0000${c.charCodeAt(0).toString(16)}`.slice(-4)}`;
1455
- },
1456
-
1457
- escape(value) {
1458
- if (isString(value))
1459
- return `'${value.replace(this.stringEscapeRegex, this.stringEscapeFn)}'`;
1460
- if (isNumber(value)) return value.toString();
1461
- if (value === true) return "true";
1462
- if (value === false) return "false";
1463
- if (value === null) return "null";
1464
- if (typeof value === "undefined") return "undefined";
1465
-
1466
- throw $parseMinErr("esc", "IMPOSSIBLE");
1467
- },
1468
-
1469
- nextId(skip, init) {
1470
- const id = `v${this.state.nextId++}`;
1471
- if (!skip) {
1472
- this.current().vars.push(id + (init ? `=${init}` : ""));
1473
- }
1474
- return id;
1475
- },
1476
-
1477
- current() {
1478
- return this.state[this.state.computing];
1479
- },
1480
- };
1481
-
1482
- function ASTInterpreter($filter) {
1483
- this.$filter = $filter;
1484
- }
1485
-
1486
- ASTInterpreter.prototype = {
1487
- compile(ast) {
1488
- const self = this;
1489
- findConstantAndWatchExpressions(ast, self.$filter);
1490
- let assignable;
1491
- let assign;
1492
- if ((assignable = assignableAST(ast))) {
1493
- assign = this.recurse(assignable);
1494
- }
1495
- const toWatch = getInputs(ast.body);
1496
- let inputs;
1497
- if (toWatch) {
1498
- inputs = [];
1499
- forEach(toWatch, (watch, key) => {
1500
- const input = self.recurse(watch);
1501
- input.isPure = watch.isPure;
1502
- watch.input = input;
1503
- inputs.push(input);
1504
- watch.watchId = key;
1505
- });
1506
- }
1507
- const expressions = [];
1508
- forEach(ast.body, (expression) => {
1509
- expressions.push(self.recurse(expression.expression));
1510
- });
1511
- const fn =
1512
- ast.body.length === 0
1513
- ? () => {}
1514
- : ast.body.length === 1
1515
- ? expressions[0]
1516
- : function (scope, locals) {
1517
- let lastValue;
1518
- forEach(expressions, (exp) => {
1519
- lastValue = exp(scope, locals);
1520
- });
1521
- return lastValue;
1522
- };
1523
- if (assign) {
1524
- fn.assign = function (scope, value, locals) {
1525
- return assign(scope, locals, value);
1526
- };
1527
- }
1528
- if (inputs) {
1529
- fn.inputs = inputs;
1530
- }
1531
- return fn;
1532
- },
1533
-
1534
- recurse(ast, context, create) {
1535
- let left;
1536
- let right;
1537
- const self = this;
1538
- let args;
1539
- if (ast.input) {
1540
- return this.inputs(ast.input, ast.watchId);
1541
- }
1542
- switch (ast.type) {
1543
- case ASTType.Literal:
1544
- return this.value(ast.value, context);
1545
- case ASTType.UnaryExpression:
1546
- right = this.recurse(ast.argument);
1547
- return this[`unary${ast.operator}`](right, context);
1548
- case ASTType.BinaryExpression:
1549
- left = this.recurse(ast.left);
1550
- right = this.recurse(ast.right);
1551
- return this[`binary${ast.operator}`](left, right, context);
1552
- case ASTType.LogicalExpression:
1553
- left = this.recurse(ast.left);
1554
- right = this.recurse(ast.right);
1555
- return this[`binary${ast.operator}`](left, right, context);
1556
- case ASTType.ConditionalExpression:
1557
- return this["ternary?:"](
1558
- this.recurse(ast.test),
1559
- this.recurse(ast.alternate),
1560
- this.recurse(ast.consequent),
1561
- context,
1562
- );
1563
- case ASTType.Identifier:
1564
- return self.identifier(ast.name, context, create);
1565
- case ASTType.MemberExpression:
1566
- left = this.recurse(ast.object, false, !!create);
1567
- if (!ast.computed) {
1568
- right = ast.property.name;
1569
- }
1570
- if (ast.computed) right = this.recurse(ast.property);
1571
- return ast.computed
1572
- ? this.computedMember(left, right, context, create)
1573
- : this.nonComputedMember(left, right, context, create);
1574
- case ASTType.CallExpression:
1575
- args = [];
1576
- forEach(ast.arguments, (expr) => {
1577
- args.push(self.recurse(expr));
1578
- });
1579
- if (ast.filter) right = this.$filter(ast.callee.name);
1580
- if (!ast.filter) right = this.recurse(ast.callee, true);
1581
- return ast.filter
1582
- ? function (scope, locals, assign, inputs) {
1583
- const values = [];
1584
- for (let i = 0; i < args.length; ++i) {
1585
- values.push(args[i](scope, locals, assign, inputs));
1586
- }
1587
- const value = right.apply(undefined, values, inputs);
1588
- return context
1589
- ? { context: undefined, name: undefined, value }
1590
- : value;
1591
- }
1592
- : function (scope, locals, assign, inputs) {
1593
- const rhs = right(scope, locals, assign, inputs);
1594
- let value;
1595
- if (rhs.value != null) {
1596
- const values = [];
1597
- for (let i = 0; i < args.length; ++i) {
1598
- values.push(args[i](scope, locals, assign, inputs));
1599
- }
1600
- value = rhs.value.apply(rhs.context, values);
1601
- }
1602
- return context ? { value } : value;
1603
- };
1604
- case ASTType.AssignmentExpression:
1605
- left = this.recurse(ast.left, true, 1);
1606
- right = this.recurse(ast.right);
1607
- return function (scope, locals, assign, inputs) {
1608
- const lhs = left(scope, locals, assign, inputs);
1609
- const rhs = right(scope, locals, assign, inputs);
1610
- lhs.context[lhs.name] = rhs;
1611
- return context ? { value: rhs } : rhs;
1612
- };
1613
- case ASTType.ArrayExpression:
1614
- args = [];
1615
- forEach(ast.elements, (expr) => {
1616
- args.push(self.recurse(expr));
1617
- });
1618
- return function (scope, locals, assign, inputs) {
1619
- const value = [];
1620
- for (let i = 0; i < args.length; ++i) {
1621
- value.push(args[i](scope, locals, assign, inputs));
1622
- }
1623
- return context ? { value } : value;
1624
- };
1625
- case ASTType.ObjectExpression:
1626
- args = [];
1627
- forEach(ast.properties, (property) => {
1628
- if (property.computed) {
1629
- args.push({
1630
- key: self.recurse(property.key),
1631
- computed: true,
1632
- value: self.recurse(property.value),
1633
- });
1634
- } else {
1635
- args.push({
1636
- key:
1637
- property.key.type === ASTType.Identifier
1638
- ? property.key.name
1639
- : `${property.key.value}`,
1640
- computed: false,
1641
- value: self.recurse(property.value),
1642
- });
1643
- }
1644
- });
1645
- return function (scope, locals, assign, inputs) {
1646
- const value = {};
1647
- for (let i = 0; i < args.length; ++i) {
1648
- if (args[i].computed) {
1649
- value[args[i].key(scope, locals, assign, inputs)] = args[i].value(
1650
- scope,
1651
- locals,
1652
- assign,
1653
- inputs,
1654
- );
1655
- } else {
1656
- value[args[i].key] = args[i].value(scope, locals, assign, inputs);
1657
- }
1658
- }
1659
- return context ? { value } : value;
1660
- };
1661
- case ASTType.ThisExpression:
1662
- return function (scope) {
1663
- return context ? { value: scope } : scope;
1664
- };
1665
- case ASTType.LocalsExpression:
1666
- return function (scope, locals) {
1667
- return context ? { value: locals } : locals;
1668
- };
1669
- case ASTType.NGValueParameter:
1670
- return function (scope, locals, assign) {
1671
- return context ? { value: assign } : assign;
1672
- };
1673
- }
1674
- },
1675
-
1676
- "unary+": function (argument, context) {
1677
- return function (scope, locals, assign, inputs) {
1678
- let arg = argument(scope, locals, assign, inputs);
1679
- if (isDefined(arg)) {
1680
- arg = +arg;
1681
- } else {
1682
- arg = 0;
1683
- }
1684
- return context ? { value: arg } : arg;
1685
- };
1686
- },
1687
- "unary-": function (argument, context) {
1688
- return function (scope, locals, assign, inputs) {
1689
- let arg = argument(scope, locals, assign, inputs);
1690
- if (isDefined(arg)) {
1691
- arg = -arg;
1692
- } else {
1693
- arg = -0;
1694
- }
1695
- return context ? { value: arg } : arg;
1696
- };
1697
- },
1698
- "unary!": function (argument, context) {
1699
- return function (scope, locals, assign, inputs) {
1700
- const arg = !argument(scope, locals, assign, inputs);
1701
- return context ? { value: arg } : arg;
1702
- };
1703
- },
1704
- "binary+": function (left, right, context) {
1705
- return function (scope, locals, assign, inputs) {
1706
- const lhs = left(scope, locals, assign, inputs);
1707
- const rhs = right(scope, locals, assign, inputs);
1708
- const arg = plusFn(lhs, rhs);
1709
- return context ? { value: arg } : arg;
1710
- };
1711
- },
1712
- "binary-": function (left, right, context) {
1713
- return function (scope, locals, assign, inputs) {
1714
- const lhs = left(scope, locals, assign, inputs);
1715
- const rhs = right(scope, locals, assign, inputs);
1716
- const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
1717
- return context ? { value: arg } : arg;
1718
- };
1719
- },
1720
- "binary*": function (left, right, context) {
1721
- return function (scope, locals, assign, inputs) {
1722
- const arg =
1723
- left(scope, locals, assign, inputs) *
1724
- right(scope, locals, assign, inputs);
1725
- return context ? { value: arg } : arg;
1726
- };
1727
- },
1728
- "binary/": function (left, right, context) {
1729
- return function (scope, locals, assign, inputs) {
1730
- const arg =
1731
- left(scope, locals, assign, inputs) /
1732
- right(scope, locals, assign, inputs);
1733
- return context ? { value: arg } : arg;
1734
- };
1735
- },
1736
- "binary%": function (left, right, context) {
1737
- return function (scope, locals, assign, inputs) {
1738
- const arg =
1739
- left(scope, locals, assign, inputs) %
1740
- right(scope, locals, assign, inputs);
1741
- return context ? { value: arg } : arg;
1742
- };
1743
- },
1744
- "binary===": function (left, right, context) {
1745
- return function (scope, locals, assign, inputs) {
1746
- const arg =
1747
- left(scope, locals, assign, inputs) ===
1748
- right(scope, locals, assign, inputs);
1749
- return context ? { value: arg } : arg;
1750
- };
1751
- },
1752
- "binary!==": function (left, right, context) {
1753
- return function (scope, locals, assign, inputs) {
1754
- const arg =
1755
- left(scope, locals, assign, inputs) !==
1756
- right(scope, locals, assign, inputs);
1757
- return context ? { value: arg } : arg;
1758
- };
1759
- },
1760
- "binary==": function (left, right, context) {
1761
- return function (scope, locals, assign, inputs) {
1762
- const arg =
1763
- left(scope, locals, assign, inputs) ==
1764
- right(scope, locals, assign, inputs);
1765
- return context ? { value: arg } : arg;
1766
- };
1767
- },
1768
- "binary!=": function (left, right, context) {
1769
- return function (scope, locals, assign, inputs) {
1770
- const arg =
1771
- left(scope, locals, assign, inputs) !=
1772
- right(scope, locals, assign, inputs);
1773
- return context ? { value: arg } : arg;
1774
- };
1775
- },
1776
- "binary<": function (left, right, context) {
1777
- return function (scope, locals, assign, inputs) {
1778
- const arg =
1779
- left(scope, locals, assign, inputs) <
1780
- right(scope, locals, assign, inputs);
1781
- return context ? { value: arg } : arg;
1782
- };
1783
- },
1784
- "binary>": function (left, right, context) {
1785
- return function (scope, locals, assign, inputs) {
1786
- const arg =
1787
- left(scope, locals, assign, inputs) >
1788
- right(scope, locals, assign, inputs);
1789
- return context ? { value: arg } : arg;
1790
- };
1791
- },
1792
- "binary<=": function (left, right, context) {
1793
- return function (scope, locals, assign, inputs) {
1794
- const arg =
1795
- left(scope, locals, assign, inputs) <=
1796
- right(scope, locals, assign, inputs);
1797
- return context ? { value: arg } : arg;
1798
- };
1799
- },
1800
- "binary>=": function (left, right, context) {
1801
- return function (scope, locals, assign, inputs) {
1802
- const arg =
1803
- left(scope, locals, assign, inputs) >=
1804
- right(scope, locals, assign, inputs);
1805
- return context ? { value: arg } : arg;
1806
- };
1807
- },
1808
- "binary&&": function (left, right, context) {
1809
- return function (scope, locals, assign, inputs) {
1810
- const arg =
1811
- left(scope, locals, assign, inputs) &&
1812
- right(scope, locals, assign, inputs);
1813
- return context ? { value: arg } : arg;
1814
- };
1815
- },
1816
- "binary||": function (left, right, context) {
1817
- return function (scope, locals, assign, inputs) {
1818
- const arg =
1819
- left(scope, locals, assign, inputs) ||
1820
- right(scope, locals, assign, inputs);
1821
- return context ? { value: arg } : arg;
1822
- };
1823
- },
1824
- "ternary?:": function (test, alternate, consequent, context) {
1825
- return function (scope, locals, assign, inputs) {
1826
- const arg = test(scope, locals, assign, inputs)
1827
- ? alternate(scope, locals, assign, inputs)
1828
- : consequent(scope, locals, assign, inputs);
1829
- return context ? { value: arg } : arg;
1830
- };
1831
- },
1832
- value(value, context) {
1833
- return function () {
1834
- return context ? { context: undefined, name: undefined, value } : value;
1835
- };
1836
- },
1837
- identifier(name, context, create) {
1838
- return function (scope, locals) {
1839
- const base = locals && name in locals ? locals : scope;
1840
- if (create && create !== 1 && base && base[name] == null) {
1841
- base[name] = {};
1842
- }
1843
- const value = base ? base[name] : undefined;
1844
- if (context) {
1845
- return { context: base, name, value };
1846
- }
1847
- return value;
1848
- };
1849
- },
1850
- computedMember(left, right, context, create) {
1851
- return function (scope, locals, assign, inputs) {
1852
- const lhs = left(scope, locals, assign, inputs);
1853
- let rhs;
1854
- let value;
1855
- if (lhs != null) {
1856
- rhs = right(scope, locals, assign, inputs);
1857
- rhs = getStringValue(rhs);
1858
- if (create && create !== 1) {
1859
- if (lhs && !lhs[rhs]) {
1860
- lhs[rhs] = {};
1861
- }
1862
- }
1863
- value = lhs[rhs];
1864
- }
1865
- if (context) {
1866
- return { context: lhs, name: rhs, value };
1867
- }
1868
- return value;
1869
- };
1870
- },
1871
- nonComputedMember(left, right, context, create) {
1872
- return function (scope, locals, assign, inputs) {
1873
- const lhs = left(scope, locals, assign, inputs);
1874
- if (create && create !== 1) {
1875
- if (lhs && lhs[right] == null) {
1876
- lhs[right] = {};
1877
- }
1878
- }
1879
- const value = lhs != null ? lhs[right] : undefined;
1880
- if (context) {
1881
- return { context: lhs, name: right, value };
1882
- }
1883
- return value;
1884
- };
1885
- },
1886
- inputs(input, watchId) {
1887
- return function (scope, value, locals, inputs) {
1888
- if (inputs) return inputs[watchId];
1889
- return input(scope, value, locals);
1890
- };
1891
- },
1892
- };
1893
-
1894
- /**
1895
- * @constructor
1896
- */
1897
- class Parser {
1898
- constructor(lexer, $filter, options) {
1899
- this.ast = new AST(lexer, options);
1900
- this.astCompiler = options.csp
1901
- ? new ASTInterpreter($filter)
1902
- : new ASTCompiler($filter);
1903
- }
1904
-
1905
- parse(text) {
1906
- const { ast, oneTime } = this.getAst(text);
1907
- const fn = this.astCompiler.compile(ast);
1908
- fn.literal = isLiteral(ast);
1909
- fn.constant = isConstant(ast);
1910
- fn.oneTime = oneTime;
1911
- return fn;
1912
- }
1913
-
1914
- getAst(exp) {
1915
- let oneTime = false;
1916
- exp = exp.trim();
1917
-
1918
- if (exp.startsWith("::")) {
1919
- oneTime = true;
1920
- exp = exp.substring(2);
1921
- }
1922
- return {
1923
- ast: this.ast.ast(exp),
1924
- oneTime,
1925
- };
1926
- }
1927
- }
1928
-
1929
- function getValueOf(value) {
1930
- return isFunction(value.valueOf)
1931
- ? value.valueOf()
1932
- : objectValueOf.call(value);
1933
- }
12
+ export const $parseMinErr = minErr("$parse");
1934
13
 
1935
14
  /// ////////////////////////////////
1936
15
 
1937
16
  /**
1938
- * @ngdoc service
1939
- * @name $parse
1940
- * @kind function
1941
- *
1942
- * @description
1943
- *
1944
- * Converts AngularJS {@link guide/expression expression} into a function.
1945
- *
1946
- * ```js
1947
- * let getter = $parse('user.name');
1948
- * let setter = getter.assign;
1949
- * let context = {user:{name:'AngularJS'}};
1950
- * let locals = {user:{name:'local'}};
1951
- *
1952
- * expect(getter(context)).toEqual('AngularJS');
1953
- * setter(context, 'newValue');
1954
- * expect(context.user.name).toEqual('newValue');
1955
- * expect(getter(context, locals)).toEqual('local');
1956
- * ```
1957
- *
1958
- *
1959
- * @param {string} expression String expression to compile.
1960
- * @returns {function(context, locals)} a function which represents the compiled expression:
1961
- *
1962
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
1963
- * are evaluated against (typically a scope object).
1964
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
1965
- * `context`.
1966
- *
1967
- * The returned function also has the following properties:
1968
- * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
1969
- * literal.
1970
- * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
1971
- * constant literals.
1972
- * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
1973
- * set to a function to change its value on the given context.
1974
- *
17
+ * @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): import('../../types').CompiledExpression} ParseService
1975
18
  */
1976
19
 
1977
20
  export const literals = {
@@ -1981,15 +24,6 @@ export const literals = {
1981
24
  undefined,
1982
25
  };
1983
26
 
1984
- /**
1985
- * @ngdoc provider
1986
- * @name $parseProvider
1987
- *
1988
- *
1989
- * @description
1990
- * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
1991
- * service.
1992
- */
1993
27
  export function $ParseProvider() {
1994
28
  const cache = Object.create(null);
1995
29
  const literals = {
@@ -1998,7 +32,11 @@ export function $ParseProvider() {
1998
32
  null: null,
1999
33
  undefined: undefined,
2000
34
  };
2001
- var identStart, identContinue;
35
+ /** @type {function(any):boolean?} */
36
+ var identStart;
37
+
38
+ /** @type {function(any):boolean?} */
39
+ var identContinue;
2002
40
 
2003
41
  /**
2004
42
  * @ngdoc method
@@ -2034,9 +72,9 @@ export function $ParseProvider() {
2034
72
  * Since this function will be called extensively, keep the implementation of these functions fast,
2035
73
  * as the performance of these functions have a direct impact on the expressions parsing speed.
2036
74
  *
2037
- * @param {function=} identifierStart The function that will decide whether the given character is
75
+ * @param {function(any):boolean=} identifierStart The function that will decide whether the given character is
2038
76
  * a valid identifier start character.
2039
- * @param {function=} identifierContinue The function that will decide whether the given character is
77
+ * @param {function(any):boolean=} identifierContinue The function that will decide whether the given character is
2040
78
  * a valid identifier continue character.
2041
79
  */
2042
80
  this.setIdentifierFns = function (identifierStart, identifierContinue) {
@@ -2069,7 +107,10 @@ export function $ParseProvider() {
2069
107
  parsedExpression = cache[cacheKey];
2070
108
 
2071
109
  if (!parsedExpression) {
2072
- var lexer = new Lexer($parseOptions);
110
+ var lexer = new Lexer({
111
+ isIdentifierContinue: $parseOptions.isIdentifierContinue,
112
+ isIdentifierStart: $parseOptions.isIdentifierStart,
113
+ });
2073
114
  var parser = new Parser(lexer, $filter, $parseOptions);
2074
115
  parsedExpression = parser.parse(exp);
2075
116
 
@@ -2116,7 +157,7 @@ export function $ParseProvider() {
2116
157
  }
2117
158
 
2118
159
  //Primitive or NaN
2119
- // eslint-disable-next-line no-self-compare
160
+
2120
161
  return (
2121
162
  newValue === oldValueOfValue ||
2122
163
  (newValue !== newValue && oldValueOfValue !== oldValueOfValue)
@@ -2393,7 +434,7 @@ export function inputsWatchDelegate(
2393
434
 
2394
435
  if (inputExpressions.length === 1) {
2395
436
  let oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
2396
- // eslint-disable-next-line prefer-destructuring
437
+
2397
438
  inputExpressions = inputExpressions[0];
2398
439
  return scope.$watch(
2399
440
  ($scope) => {
@@ -2428,12 +469,10 @@ export function inputsWatchDelegate(
2428
469
  (scope) => {
2429
470
  let changed = false;
2430
471
 
2431
- // eslint-disable-next-line no-plusplus
2432
472
  for (let i = 0, ii = inputExpressions.length; i < ii; i++) {
2433
473
  const newInputValue = inputExpressions[i](scope);
2434
474
  if (
2435
475
  changed ||
2436
- // eslint-disable-next-line no-cond-assign
2437
476
  (changed = !expressionInputDirtyCheck(
2438
477
  newInputValue,
2439
478
  oldInputValueOfValues[i],
@@ -2516,7 +555,8 @@ export function chainInterceptors(first, second) {
2516
555
  return chainedInterceptor;
2517
556
  }
2518
557
 
2519
- export function expressionInputDirtyCheck(
558
+ /** @private */
559
+ function expressionInputDirtyCheck(
2520
560
  newValue,
2521
561
  oldValueOfValue,
2522
562
  compareObjectIdentity,
@@ -2541,16 +581,15 @@ export function expressionInputDirtyCheck(
2541
581
  }
2542
582
 
2543
583
  // Primitive or NaN
2544
- // eslint-disable-next-line no-self-compare
584
+
2545
585
  return (
2546
586
  newValue === oldValueOfValue ||
2547
- // eslint-disable-next-line no-self-compare
2548
587
  (newValue !== newValue && oldValueOfValue !== oldValueOfValue)
2549
588
  );
2550
589
  }
2551
590
 
2552
- // eslint-disable-next-line class-methods-use-this
2553
- export function isAllDefined(value) {
591
+ /** @private */
592
+ function isAllDefined(value) {
2554
593
  let allDefined = true;
2555
594
  forEach(value, (val) => {
2556
595
  if (!isDefined(val)) allDefined = false;