From 392f18e35b919e6095245002c93f5d11085288c0 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 --- src/ng/parse.js | 37 +++++++++++++++++++++++++++---------- test/ng/parseSpec.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/ng/parse.js b/src/ng/parse.js index 9f8621440cf3..91a1b363e6c4 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,14 +1800,9 @@ 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); - parsedExpression.oneTime = !!oneTime; cache[cacheKey] = addWatchDelegate(parsedExpression); } @@ -1805,6 +1816,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 7c5baad1630b..5142bc22a7ed 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -4245,4 +4245,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 + } + } + ] + }); + })); + }); + }); });