Skip to content

Commit 8154924

Browse files
crisbetokirjs
authored andcommitted
refactor(compiler): add spread elements to expression AST
Updates the expression AST to have support for spread elements inside object literals.
1 parent a789ff0 commit 8154924

8 files changed

Lines changed: 43 additions & 11 deletions

File tree

packages/compiler/src/compiler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ export {
116116
UnaryOperatorExpr,
117117
VoidExpr,
118118
WrappedNodeExpr,
119+
LiteralMapPropertyAssignment,
120+
LiteralMapSpreadAssignment,
119121
} from './output/output_ast';
120122
export {JitEvaluator} from './output/output_jit';
121123
export {SourceMap} from './output/source_map';

packages/compiler/src/expression_parser/ast.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,23 @@ export class LiteralArray extends AST {
210210
}
211211
}
212212

213-
export interface LiteralMapKey {
213+
export interface LiteralMapPropertyKey {
214+
kind: 'property';
214215
key: string;
215216
quoted: boolean;
216217
span: ParseSpan;
217218
sourceSpan: AbsoluteSourceSpan;
218219
isShorthandInitialized?: boolean;
219220
}
220221

222+
export interface LiteralMapSpreadKey {
223+
kind: 'spread';
224+
span: ParseSpan;
225+
sourceSpan: AbsoluteSourceSpan;
226+
}
227+
228+
export type LiteralMapKey = LiteralMapPropertyKey | LiteralMapSpreadKey;
229+
221230
export class LiteralMap extends AST {
222231
constructor(
223232
span: ParseSpan,

packages/compiler/src/expression_parser/parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,7 @@ class _ParseAST {
12001200
const keySpan = this.span(keyStart);
12011201
const keySourceSpan = this.sourceSpan(keyStart);
12021202
const literalMapKey: LiteralMapKey = {
1203+
kind: 'property',
12031204
key,
12041205
quoted,
12051206
span: keySpan,

packages/compiler/src/expression_parser/serializer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,12 @@ class SerializeExpressionVisitor implements expr.AstVisitor {
5858

5959
visitLiteralMap(ast: expr.LiteralMap, context: any): string {
6060
return `{${zip(
61-
ast.keys.map((literal) => (literal.quoted ? `'${literal.key}'` : literal.key)),
61+
ast.keys.map((literal) => {
62+
if (literal.kind === 'spread') {
63+
return '...';
64+
}
65+
return literal.quoted ? `'${literal.key}'` : literal.key;
66+
}),
6267
ast.values.map((value) => value.visit(this, context)),
6368
)
6469
.map(([key, value]) => `${key}: ${value}`)

packages/compiler/src/render3/r3_deferred_triggers.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,13 +616,17 @@ function createViewportTrigger(
616616

617617
if (!(parsed.ast instanceof LiteralMap)) {
618618
throw new Error('Options parameter of the "viewport" trigger must be an object literal');
619-
} else if (parsed.ast.keys.some((key) => key.key === 'root')) {
619+
} else if (parsed.ast.keys.some((key) => key.kind === 'spread')) {
620+
throw new Error('Spread operator are not allowed in this context');
621+
} else if (parsed.ast.keys.some((key) => key.kind === 'property' && key.key === 'root')) {
620622
throw new Error(
621623
'The "root" option is not supported in the options parameter of the "viewport" trigger',
622624
);
623625
}
624626

625-
const triggerIndex = parsed.ast.keys.findIndex((key) => key.key === 'trigger');
627+
const triggerIndex = parsed.ast.keys.findIndex(
628+
(key) => key.kind === 'property' && key.key === 'trigger',
629+
);
626630

627631
if (triggerIndex === -1) {
628632
reference = null;

packages/compiler/src/template/pipeline/src/ingest.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,10 +1140,13 @@ function convertAst(
11401140
throw new Error(`AssertionError: Chain in unknown context`);
11411141
} else if (ast instanceof e.LiteralMap) {
11421142
const entries = ast.keys.map((key, idx) => {
1143-
const value = ast.values[idx];
1143+
const value = convertAst(ast.values[idx], job, baseSourceSpan);
1144+
11441145
// TODO: should literals have source maps, or do we just map the whole surrounding
11451146
// expression?
1146-
return new o.LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted);
1147+
return key.kind === 'spread'
1148+
? new o.LiteralMapSpreadAssignment(value)
1149+
: new o.LiteralMapPropertyAssignment(key.key, value, key.quoted);
11471150
});
11481151
return new o.LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
11491152
} else if (ast instanceof e.LiteralArray) {

packages/compiler/test/expression_parser/parser_spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
VariableBinding,
2020
BindingPipeType,
2121
ParseSpan,
22+
LiteralMapPropertyKey,
2223
} from '../../src/expression_parser/ast';
2324
import {ParseError} from '../../src/parse_util';
2425
import {Lexer} from '../../src/expression_parser/lexer';
@@ -914,9 +915,10 @@ describe('parser', () => {
914915
it('should expose object shorthand information in AST', () => {
915916
const parser = new Parser(new Lexer());
916917
const ast = parser.parseBinding('{bla}', getFakeSpan(), 0);
917-
expect(ast.ast instanceof LiteralMap).toBe(true);
918-
expect((ast.ast as LiteralMap).keys.length).toBe(1);
919-
expect((ast.ast as LiteralMap).keys[0].isShorthandInitialized).toBe(true);
918+
const map = ast.ast as LiteralMap;
919+
expect(map instanceof LiteralMap).toBe(true);
920+
expect(map.keys.length).toBe(1);
921+
expect((map.keys[0] as LiteralMapPropertyKey).isShorthandInitialized).toBe(true);
920922
});
921923
});
922924

packages/compiler/test/expression_parser/utils/unparser.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,14 @@ class Unparser implements AstVisitor {
161161
if (!isFirst) this._expression += ', ';
162162
isFirst = false;
163163
const key = ast.keys[i];
164-
this._expression += key.quoted ? JSON.stringify(key.key) : key.key;
165-
this._expression += ': ';
164+
165+
if (key.kind === 'spread') {
166+
this._expression += '...';
167+
} else {
168+
this._expression += key.quoted ? JSON.stringify(key.key) : key.key;
169+
this._expression += ': ';
170+
}
171+
166172
this._visit(ast.values[i]);
167173
}
168174

0 commit comments

Comments
 (0)