@@ -2,6 +2,8 @@ import read_context from '../read/context.js';
22import read_expression from '../read/expression.js' ;
33import { error } from '../../../errors.js' ;
44import { create_fragment } from '../utils/create.js' ;
5+ import { parse_expression_at } from '../acorn.js' ;
6+ import { walk } from 'zimmerframe' ;
57
68const regex_whitespace_with_closing_curly_brace = / ^ \s * } / ;
79
@@ -67,10 +69,73 @@ function open(parser) {
6769 if ( parser . eat ( 'each' ) ) {
6870 parser . require_whitespace ( ) ;
6971
70- const expression = read_expression ( parser ) ;
72+ const template = parser . template ;
73+ let end = parser . template . length ;
74+
75+ /** @type {import('estree').Expression | undefined } */
76+ let expression ;
77+
78+ // we have to do this loop because `{#each x as { y = z }}` fails to parse —
79+ // the `as { y = z }` is treated as an Expression but it's actually a Pattern.
80+ // the 'fix' is to backtrack and hide everything from the `as` onwards, until
81+ // we get a valid expression
82+ while ( ! expression ) {
83+ try {
84+ expression = read_expression ( parser ) ;
85+ } catch ( err ) {
86+ end = /** @type {any } */ ( err ) . position [ 0 ] - 2 ;
87+
88+ while ( end > start && parser . template . slice ( end , end + 2 ) !== 'as' ) {
89+ end -= 1 ;
90+ }
91+
92+ if ( end <= start ) throw err ;
93+
94+ // @ts -expect-error parser.template is meant to be readonly, this is a special case
95+ parser . template = template . slice ( 0 , end ) ;
96+ }
97+ }
98+
99+ // @ts -expect-error
100+ parser . template = template ;
101+
71102 parser . allow_whitespace ( ) ;
72103
73104 // {#each} blocks must declare a context – {#each list as item}
105+ if ( ! parser . match ( 'as' ) ) {
106+ // this could be a TypeScript assertion that was erroneously eaten.
107+
108+ if ( expression . type === 'SequenceExpression' ) {
109+ expression = expression . expressions [ 0 ] ;
110+ }
111+
112+ let assertion = null ;
113+ let end = expression . end ;
114+
115+ expression = walk ( expression , null , {
116+ // @ts -expect-error
117+ TSAsExpression ( node , context ) {
118+ if ( node . end === /** @type {import('estree').Expression } */ ( expression ) . end ) {
119+ assertion = node ;
120+ end = node . expression . end ;
121+ return node . expression ;
122+ }
123+
124+ context . next ( ) ;
125+ }
126+ } ) ;
127+
128+ expression . end = end ;
129+
130+ if ( assertion ) {
131+ // we can't reset `parser.index` to `expression.expression.end` because
132+ // it will ignore any parentheses — we need to jump through this hoop
133+ let end = /** @type {any } */ ( /** @type {any } */ ( assertion ) . typeAnnotation ) . start - 2 ;
134+ while ( parser . template . slice ( end , end + 2 ) !== 'as' ) end -= 1 ;
135+
136+ parser . index = end ;
137+ }
138+ }
74139 parser . eat ( 'as' , true ) ;
75140 parser . require_whitespace ( ) ;
76141
0 commit comments