@angular-wave/angular.ts 0.0.49 → 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,6 +1,6 @@
1
- import { $parseMinErr } from "./parse";
1
+ import { isDefined, minErr } from "../../shared/utils";
2
2
 
3
- import { isDefined } from "../../shared/utils";
3
+ const $parseMinErr = minErr("$parse");
4
4
 
5
5
  const ESCAPE = {
6
6
  n: "\n",
@@ -18,19 +18,19 @@ const OPERATORS = new Set(
18
18
 
19
19
  /**
20
20
  * @typedef {Object} LexerOptions
21
- * @property {(ch: string, codePoint: number) => boolean} [isIdentifierStart] Custom function to determine if a character is a valid identifier start.
22
- * @property {(ch: string, codePoint: number) => boolean} [isIdentifierContinue] Custom function to determine if a character is a valid identifier continuation.
21
+ * @property {(ch: string, codePoint: number) => boolean} [isIdentifierStart] - Custom function to determine if a character is a valid identifier start.
22
+ * @property {(ch: string, codePoint: number) => boolean} [isIdentifierContinue] - Custom function to determine if a character is a valid identifier continuation.
23
23
  */
24
24
 
25
25
  /**
26
- * Represents a token produced by the lexer.
26
+ * Represents a token produced by the lexer, which will be used by the AST to construct an abstract syntax tree.
27
27
  * @typedef {Object} Token
28
- * @property {number} index Index of the token.
29
- * @property {string} text Text of the token.
30
- * @property {boolean} [identifier] Indicates if token is an identifier.
31
- * @property {boolean} [constant] Indicates if token is a constant.
32
- * @property {string|number} [value] Value of the token if it's a constant.
33
- * @property {boolean} [operator] Indicates if token is an operator.
28
+ * @property {number} index - Index of the token.
29
+ * @property {string} text - Text of the token.
30
+ * @property {boolean} [identifier] - Indicates if token is an identifier.
31
+ * @property {boolean} [constant] - Indicates if token is a constant.
32
+ * @property {string|number} [value] - Value of the token if it's a constant.
33
+ * @property {boolean} [operator] - Indicates if token is an operator.
34
34
  */
35
35
 
36
36
  /**
@@ -40,7 +40,7 @@ const OPERATORS = new Set(
40
40
  export class Lexer {
41
41
  /**
42
42
  * Creates an instance of Lexer.
43
- * @param {LexerOptions} options Lexer options.
43
+ * @param {LexerOptions} options - Lexer options.
44
44
  */
45
45
  constructor(options) {
46
46
  /** @type {LexerOptions} */
@@ -67,7 +67,10 @@ export class Lexer {
67
67
  (ch === "." && this.isNumber(/** @type {string} */ (this.peek())))
68
68
  ) {
69
69
  this.readNumber();
70
- } else if (this.isIdentifierStart(this.peekMultichar())) {
70
+ } else if (
71
+ this.isIdentifierStart &&
72
+ this.isIdentifierStart(this.peekMultichar())
73
+ ) {
71
74
  this.readIdent();
72
75
  } else if (this.is(ch, "(){}[].,;:?")) {
73
76
  this.tokens.push({ index: this.index, text: ch });
@@ -133,14 +136,8 @@ export class Lexer {
133
136
  * @returns {boolean} True if character is whitespace, false otherwise.
134
137
  */
135
138
  isWhitespace(ch) {
136
- // IE treats non-breaking space as \u00A0
137
139
  return (
138
- ch === " " ||
139
- ch === "\r" ||
140
- ch === "\t" ||
141
- ch === "\n" ||
142
- ch === "\v" ||
143
- ch === "\u00A0"
140
+ ch === " " || ch === "\r" || ch === "\t" || ch === "\n" || ch === "\v"
144
141
  );
145
142
  }
146
143
 
@@ -180,7 +177,6 @@ export class Lexer {
180
177
  */
181
178
  codePointAt(ch) {
182
179
  if (ch.length === 1) return ch.charCodeAt(0);
183
-
184
180
  return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
185
181
  }
186
182
 
@@ -231,6 +227,7 @@ export class Lexer {
231
227
 
232
228
  /**
233
229
  * Reads and tokenizes a number from the text.
230
+ * @return {void}
234
231
  */
235
232
  readNumber() {
236
233
  let number = "";
@@ -278,7 +275,7 @@ export class Lexer {
278
275
  this.index += this.peekMultichar().length;
279
276
  while (this.index < this.text.length) {
280
277
  const ch = this.peekMultichar();
281
- if (!this.isIdentifierContinue(ch)) {
278
+ if (this.isIdentifierContinue && !this.isIdentifierContinue(ch)) {
282
279
  break;
283
280
  }
284
281
  this.index += ch.length;
@@ -334,6 +331,9 @@ export class Lexer {
334
331
  this.throwError("Unterminated quote", start);
335
332
  }
336
333
 
334
+ /**
335
+ * @returns {string}
336
+ */
337
337
  handleUnicodeEscape() {
338
338
  const hex = this.text.substring(this.index + 1, this.index + 5);
339
339
  if (!hex.match(/[\da-f]{4}/i)) {
@@ -1,37 +1,40 @@
1
- import {
2
- csp,
3
- forEach,
4
- isDefined,
5
- isFunction,
6
- minErr,
7
- } from "../../shared/utils";
8
- import { getValueOf, PURITY_RELATIVE } from "./shared";
1
+ import { forEach, isDefined, isFunction } from "../../shared/utils";
2
+ import { PURITY_RELATIVE } from "./interpreter";
9
3
  import { Lexer } from "./lexer";
10
4
  import { Parser } from "./parser";
11
5
 
12
- export const $parseMinErr = minErr("$parse");
6
+ /**
7
+ * @typedef {Object} CompiledExpressionProps
8
+ * @property {boolean} literal - Indicates if the expression is a literal.
9
+ * @property {boolean} constant - Indicates if the expression is constant.
10
+ * @property {boolean} isPure
11
+ * @property {boolean} oneTime
12
+ * @property {any[]} inputs
13
+ * @property {function(any, any): any} assign - Assigns a value to a context. If value is not provided,
14
+ */
13
15
 
14
- /// ////////////////////////////////
16
+ /**
17
+ * @typedef {function } CompiledExpressionFunction
18
+ * @param {import('../scope/scope').Scope} context - An object against which any expressions embedded in the strings are evaluated against (typically a scope object).
19
+ * @param {object} [locals] - local variables context object, useful for overriding values in `context`.
20
+ * @param {any} [assign]
21
+ * @returns {any}
22
+ * undefined is gonna be used since the implementation
23
+ * does not check the parameter. Let's force a value for consistency. If consumer
24
+ * wants to undefine it, pass the undefined value explicitly.
25
+ */
15
26
 
16
27
  /**
17
- * @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): import('../../types').CompiledExpression} ParseService
28
+ * @typedef {CompiledExpressionFunction & CompiledExpressionProps} CompiledExpression
18
29
  */
19
30
 
20
- export const literals = {
21
- true: true,
22
- false: false,
23
- null: null,
24
- undefined,
25
- };
31
+ /**
32
+ * @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): CompiledExpression} ParseService
33
+ */
26
34
 
27
35
  export function $ParseProvider() {
28
36
  const cache = Object.create(null);
29
- const literals = {
30
- true: true,
31
- false: false,
32
- null: null,
33
- undefined: undefined,
34
- };
37
+
35
38
  /** @type {function(any):boolean?} */
36
39
  var identStart;
37
40
 
@@ -39,26 +42,6 @@ export function $ParseProvider() {
39
42
  var identContinue;
40
43
 
41
44
  /**
42
- * @ngdoc method
43
- * @name $parseProvider#addLiteral
44
- * @description
45
- *
46
- * Configure $parse service to add literal values that will be present as literal at expressions.
47
- *
48
- * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
49
- * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
50
- *
51
- **/
52
- this.addLiteral = function (literalName, literalValue) {
53
- literals[literalName] = literalValue;
54
- };
55
-
56
- /**
57
- * @ngdoc method
58
- * @name $parseProvider#setIdentifierFns
59
- *
60
- * @description
61
- *
62
45
  * Allows defining the set of characters that are allowed in AngularJS expressions. The function
63
46
  * `identifierStart` will get called to know if a given character is a valid character to be the
64
47
  * first character for an identifier. The function `identifierContinue` will get called to know if
@@ -72,10 +55,11 @@ export function $ParseProvider() {
72
55
  * Since this function will be called extensively, keep the implementation of these functions fast,
73
56
  * as the performance of these functions have a direct impact on the expressions parsing speed.
74
57
  *
75
- * @param {function(any):boolean=} identifierStart The function that will decide whether the given character is
58
+ * @param {function(any):boolean} [identifierStart] The function that will decide whether the given character is
76
59
  * a valid identifier start character.
77
- * @param {function(any):boolean=} identifierContinue The function that will decide whether the given character is
60
+ * @param {function(any):boolean} [identifierContinue] The function that will decide whether the given character is
78
61
  * a valid identifier continue character.
62
+ * @returns {$ParseProvider}
79
63
  */
80
64
  this.setIdentifierFns = function (identifierStart, identifierContinue) {
81
65
  identStart = identifierStart;
@@ -86,10 +70,8 @@ export function $ParseProvider() {
86
70
  this.$get = [
87
71
  "$filter",
88
72
  function ($filter) {
89
- var noUnsafeEval = csp().noUnsafeEval;
90
- var $parseOptions = {
91
- csp: noUnsafeEval,
92
- literals: structuredClone(literals),
73
+ /** @type {import("./lexer").LexerOptions} */
74
+ var $lexerOptions = {
93
75
  isIdentifierStart: isFunction(identStart) && identStart,
94
76
  isIdentifierContinue: isFunction(identContinue) && identContinue,
95
77
  };
@@ -107,11 +89,8 @@ export function $ParseProvider() {
107
89
  parsedExpression = cache[cacheKey];
108
90
 
109
91
  if (!parsedExpression) {
110
- var lexer = new Lexer({
111
- isIdentifierContinue: $parseOptions.isIdentifierContinue,
112
- isIdentifierStart: $parseOptions.isIdentifierStart,
113
- });
114
- var parser = new Parser(lexer, $filter, $parseOptions);
92
+ var lexer = new Lexer($lexerOptions);
93
+ var parser = new Parser(lexer, $filter);
115
94
  parsedExpression = parser.parse(exp);
116
95
 
117
96
  cache[cacheKey] = addWatchDelegate(parsedExpression);
@@ -126,213 +105,16 @@ export function $ParseProvider() {
126
105
  }
127
106
  }
128
107
 
108
+ /**
109
+ * @param {string} exp
110
+ * @returns {import("./ast").ASTNode}
111
+ */
129
112
  function $$getAst(exp) {
130
- var lexer = new Lexer($parseOptions);
131
- var parser = new Parser(lexer, $filter, $parseOptions);
113
+ var lexer = new Lexer($lexerOptions);
114
+ var parser = new Parser(lexer, $filter);
132
115
  return parser.getAst(exp).ast;
133
116
  }
134
117
 
135
- function expressionInputDirtyCheck(
136
- newValue,
137
- oldValueOfValue,
138
- compareObjectIdentity,
139
- ) {
140
- if (newValue == null || oldValueOfValue == null) {
141
- // null/undefined
142
- return newValue === oldValueOfValue;
143
- }
144
-
145
- if (typeof newValue === "object") {
146
- // attempt to convert the value to a primitive type
147
- // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
148
- // be cheaply dirty-checked
149
- newValue = getValueOf(newValue);
150
-
151
- if (typeof newValue === "object" && !compareObjectIdentity) {
152
- // objects/arrays are not supported - deep-watching them would be too expensive
153
- return false;
154
- }
155
-
156
- // fall-through to the primitive equality check
157
- }
158
-
159
- //Primitive or NaN
160
-
161
- return (
162
- newValue === oldValueOfValue ||
163
- (newValue !== newValue && oldValueOfValue !== oldValueOfValue)
164
- );
165
- }
166
-
167
- function inputsWatchDelegate(
168
- scope,
169
- listener,
170
- objectEquality,
171
- parsedExpression,
172
- ) {
173
- var inputExpressions = parsedExpression.inputs;
174
- var lastResult;
175
-
176
- if (inputExpressions.length === 1) {
177
- var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
178
- inputExpressions = inputExpressions[0];
179
- return scope.$watch(
180
- function expressionInputWatch(scope) {
181
- var newInputValue = inputExpressions(scope);
182
- if (
183
- !expressionInputDirtyCheck(
184
- newInputValue,
185
- oldInputValueOf,
186
- inputExpressions.isPure,
187
- )
188
- ) {
189
- lastResult = parsedExpression(scope, undefined, undefined, [
190
- newInputValue,
191
- ]);
192
- oldInputValueOf = newInputValue && getValueOf(newInputValue);
193
- }
194
- return lastResult;
195
- },
196
- listener,
197
- objectEquality,
198
- );
199
- }
200
-
201
- var oldInputValueOfValues = [];
202
- var oldInputValues = [];
203
- for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
204
- oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
205
- oldInputValues[i] = null;
206
- }
207
-
208
- return scope.$watch(
209
- function expressionInputsWatch(scope) {
210
- var changed = false;
211
-
212
- for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
213
- var newInputValue = inputExpressions[i](scope);
214
- if (
215
- changed ||
216
- (changed = !expressionInputDirtyCheck(
217
- newInputValue,
218
- oldInputValueOfValues[i],
219
- inputExpressions[i].isPure,
220
- ))
221
- ) {
222
- oldInputValues[i] = newInputValue;
223
- oldInputValueOfValues[i] =
224
- newInputValue && getValueOf(newInputValue);
225
- }
226
- }
227
-
228
- if (changed) {
229
- lastResult = parsedExpression(
230
- scope,
231
- undefined,
232
- undefined,
233
- oldInputValues,
234
- );
235
- }
236
-
237
- return lastResult;
238
- },
239
- listener,
240
- objectEquality,
241
- );
242
- }
243
-
244
- function oneTimeWatchDelegate(
245
- scope,
246
- listener,
247
- objectEquality,
248
- parsedExpression,
249
- ) {
250
- var isDone = parsedExpression.literal ? isAllDefined : isDefined;
251
- var unwatch, lastValue;
252
-
253
- var exp = parsedExpression.$$intercepted || parsedExpression;
254
- var post = parsedExpression.$$interceptor || ((x) => x);
255
-
256
- var useInputs = parsedExpression.inputs && !exp.inputs;
257
-
258
- // Propagate the literal/inputs/constant attributes
259
- // ... but not oneTime since we are handling it
260
- oneTimeWatch.literal = parsedExpression.literal;
261
- oneTimeWatch.constant = parsedExpression.constant;
262
- oneTimeWatch.inputs = parsedExpression.inputs;
263
-
264
- // Allow other delegates to run on this wrapped expression
265
- addWatchDelegate(oneTimeWatch);
266
-
267
- unwatch = scope.$watch(oneTimeWatch, listener, objectEquality);
268
-
269
- return unwatch;
270
-
271
- function unwatchIfDone() {
272
- if (isDone(lastValue)) {
273
- unwatch();
274
- }
275
- }
276
-
277
- function oneTimeWatch(scope, locals, assign, inputs) {
278
- lastValue =
279
- useInputs && inputs
280
- ? inputs[0]
281
- : exp(scope, locals, assign, inputs);
282
- if (isDone(lastValue)) {
283
- scope.$$postDigest(unwatchIfDone);
284
- }
285
- return post(lastValue);
286
- }
287
- }
288
-
289
- function isAllDefined(value) {
290
- var allDefined = true;
291
- forEach(value, function (val) {
292
- if (!isDefined(val)) allDefined = false;
293
- });
294
- return allDefined;
295
- }
296
-
297
- function constantWatchDelegate(
298
- scope,
299
- listener,
300
- objectEquality,
301
- parsedExpression,
302
- ) {
303
- var unwatch = scope.$watch(
304
- function constantWatch(scope) {
305
- unwatch();
306
- return parsedExpression(scope);
307
- },
308
- listener,
309
- objectEquality,
310
- );
311
- return unwatch;
312
- }
313
-
314
- function addWatchDelegate(parsedExpression) {
315
- if (parsedExpression.constant) {
316
- parsedExpression.$$watchDelegate = constantWatchDelegate;
317
- } else if (parsedExpression.oneTime) {
318
- parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
319
- } else if (parsedExpression.inputs) {
320
- parsedExpression.$$watchDelegate = inputsWatchDelegate;
321
- }
322
-
323
- return parsedExpression;
324
- }
325
-
326
- function chainInterceptors(first, second) {
327
- function chainedInterceptor(value) {
328
- return second(first(value));
329
- }
330
- chainedInterceptor.$stateful = first.$stateful || second.$stateful;
331
- chainedInterceptor.$$pure = first.$$pure && second.$$pure;
332
-
333
- return chainedInterceptor;
334
- }
335
-
336
118
  function addInterceptor(parsedExpression, interceptorFn) {
337
119
  if (!interceptorFn) return parsedExpression;
338
120
 
@@ -394,16 +176,16 @@ export function $ParseProvider() {
394
176
  ];
395
177
  }
396
178
 
397
- function constantWatchDelegate(
179
+ export function constantWatchDelegate(
398
180
  scope,
399
181
  listener,
400
182
  objectEquality,
401
183
  parsedExpression,
402
184
  ) {
403
185
  const unwatch = scope.$watch(
404
- ($scope) => {
186
+ () => {
405
187
  unwatch();
406
- return parsedExpression($scope);
188
+ return parsedExpression(scope);
407
189
  },
408
190
  listener,
409
191
  objectEquality,
@@ -423,7 +205,7 @@ function addWatchDelegate(parsedExpression) {
423
205
  return parsedExpression;
424
206
  }
425
207
 
426
- export function inputsWatchDelegate(
208
+ function inputsWatchDelegate(
427
209
  scope,
428
210
  listener,
429
211
  objectEquality,
@@ -500,7 +282,7 @@ export function inputsWatchDelegate(
500
282
  );
501
283
  }
502
284
 
503
- export function oneTimeWatchDelegate(
285
+ function oneTimeWatchDelegate(
504
286
  scope,
505
287
  listener,
506
288
  objectEquality,
@@ -545,7 +327,7 @@ export function oneTimeWatchDelegate(
545
327
  return unwatch;
546
328
  }
547
329
 
548
- export function chainInterceptors(first, second) {
330
+ function chainInterceptors(first, second) {
549
331
  function chainedInterceptor(value) {
550
332
  return second(first(value));
551
333
  }
@@ -555,7 +337,6 @@ export function chainInterceptors(first, second) {
555
337
  return chainedInterceptor;
556
338
  }
557
339
 
558
- /** @private */
559
340
  function expressionInputDirtyCheck(
560
341
  newValue,
561
342
  oldValueOfValue,
@@ -588,7 +369,6 @@ function expressionInputDirtyCheck(
588
369
  );
589
370
  }
590
371
 
591
- /** @private */
592
372
  function isAllDefined(value) {
593
373
  let allDefined = true;
594
374
  forEach(value, (val) => {
@@ -596,3 +376,9 @@ function isAllDefined(value) {
596
376
  });
597
377
  return allDefined;
598
378
  }
379
+
380
+ function getValueOf(value) {
381
+ return isFunction(value.valueOf)
382
+ ? value.valueOf()
383
+ : {}.constructor.prototype.valueOf.call(value);
384
+ }
@@ -22,10 +22,6 @@
22
22
  -
23
23
  - Converts AngularJS {@link guide/expression expression} into a function.
24
24
  -
25
- - ```js
26
-
27
- ```
28
-
29
25
  - let getter = $parse('user.name');
30
26
  - let setter = getter.assign;
31
27
  - let context = {user:{name:'AngularJS'}};
@@ -35,11 +31,6 @@
35
31
  - setter(context, 'newValue');
36
32
  - expect(context.user.name).toEqual('newValue');
37
33
  - expect(getter(context, locals)).toEqual('local');
38
- - ```
39
-
40
- ```
41
-
42
- -
43
34
  -
44
35
  - @param {string} expression String expression to compile.
45
36
  - @returns {function(context, locals)} a function which represents the compiled expression:
@@ -56,10 +47,6 @@
56
47
  - constant literals.
57
48
  - * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
58
49
  - set to a function to change its value on the given context.
59
- - \*/
60
-
61
- /\*\*
62
-
63
50
  - @ngdoc provider
64
51
  - @name $parseProvider
65
52
  -