From f33d95cfcff6fd0270f92a142df8794cca2013ad Mon Sep 17 00:00:00 2001 From: Francesco Pipita Date: Sat, 7 Oct 2017 18:27:20 +0200 Subject: [PATCH] feat($parse): add a hidden interface to retrieve an expression's AST This PR adds a new private method to the `$parse` service, `$$getAst`, which takes an Angular expression as its only argument and returns the computed AST. This feature is not meant to be part of the public API and might be subject to changes, so use it with caution. Closes #16253 Closes #16260 --- src/ng/parse.js | 38 ++++++++++++++++++++++++++++---------- test/ng/parseSpec.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/ng/parse.js b/src/ng/parse.js index 1cb87cc8f6b0..e49736ba2ce4 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -1644,11 +1644,26 @@ Parser.prototype = { constructor: Parser, parse: function(text) { - var ast = this.ast.ast(text); - var fn = this.astCompiler.compile(ast); - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); + var ast = this.getAst(text); + var fn = this.astCompiler.compile(ast.ast); + fn.literal = isLiteral(ast.ast); + fn.constant = isConstant(ast.ast); + fn.oneTime = ast.oneTime; return fn; + }, + + getAst: function(exp) { + var oneTime = false; + exp = exp.trim(); + + if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { + oneTime = true; + exp = exp.substring(2); + } + return { + ast: this.ast.ast(exp), + oneTime: oneTime + }; } }; @@ -1771,10 +1786,11 @@ function $ParseProvider() { isIdentifierStart: isFunction(identStart) && identStart, isIdentifierContinue: isFunction(identContinue) && identContinue }; + $parse.$$getAst = $$getAst; return $parse; function $parse(exp, interceptorFn) { - var parsedExpression, oneTime, cacheKey; + var parsedExpression, cacheKey; switch (typeof exp) { case 'string': @@ -1784,16 +1800,12 @@ function $ParseProvider() { parsedExpression = cache[cacheKey]; if (!parsedExpression) { - if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { - oneTime = true; - exp = exp.substring(2); - } var lexer = new Lexer($parseOptions); var parser = new Parser(lexer, $filter, $parseOptions); parsedExpression = parser.parse(exp); if (parsedExpression.constant) { parsedExpression.$$watchDelegate = constantWatchDelegate; - } else if (oneTime) { + } else if (parsedExpression.oneTime) { parsedExpression.$$watchDelegate = parsedExpression.literal ? oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; } else if (parsedExpression.inputs) { @@ -1811,6 +1823,12 @@ function $ParseProvider() { } } + function $$getAst(exp) { + var lexer = new Lexer($parseOptions); + var parser = new Parser(lexer, $filter, $parseOptions); + return parser.getAst(exp).ast; + } + function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) { if (newValue == null || oldValueOfValue == null) { // null/undefined diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index b57be4b69172..e08b881e0cfe 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -3994,4 +3994,48 @@ describe('parser', function() { }); }); }); + + describe('hidden/unsupported features', function() { + describe('$$getAst()', function() { + it('should be a method exposed on the `$parse` service', inject(function($parse) { + expect(isFunction($parse.$$getAst)).toBeTruthy(); + })); + + it('should accept a string expression argument and return the corresponding AST', inject(function($parse) { + var ast = $parse.$$getAst('foo.bar'); + expect(ast).toEqual({ + type: 'Program', + body: [ + { + type: 'ExpressionStatement', + expression: { + type: 'MemberExpression', + object: { type: 'Identifier', name: 'foo' }, + property: { type: 'Identifier', name: 'bar' }, + computed: false + } + } + ] + }); + })); + + it('should parse one time binding expressions', inject(function($parse) { + var ast = $parse.$$getAst('::foo.bar'); + expect(ast).toEqual({ + type: 'Program', + body: [ + { + type: 'ExpressionStatement', + expression: { + type: 'MemberExpression', + object: { type: 'Identifier', name: 'foo' }, + property: { type: 'Identifier', name: 'bar' }, + computed: false + } + } + ] + }); + })); + }); + }); });