rip-lang 3.8.10 → 3.9.1

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.
package/src/components.js CHANGED
@@ -1,8 +1,9 @@
1
1
  // Component System — Fine-grained reactive components for Rip
2
2
  //
3
- // Architecture: installComponentSupport(CodeGenerator) adds methods to the
4
- // CodeGenerator prototype, enabling component compilation. A separate
5
- // getComponentRuntime() emits runtime helpers only when components are used.
3
+ // Architecture: installComponentSupport(CodeGenerator, Lexer) adds methods to
4
+ // both prototypes — render rewriting on the Lexer, component code generation
5
+ // on the CodeGenerator. A separate getComponentRuntime() emits runtime helpers
6
+ // only when components are used.
6
7
  //
7
8
  // Naming: All render-tree generators use generate* (consistent with compiler).
8
9
 
@@ -50,11 +51,381 @@ function getMemberName(target) {
50
51
  return null;
51
52
  }
52
53
 
54
+ /**
55
+ * Detect fragment root and collect direct child variables for proper removal.
56
+ * After insertBefore, a DocumentFragment is empty — .remove() is a no-op.
57
+ * Callers must remove each child element individually.
58
+ */
59
+ function getFragChildren(rootVar, createLines, localizeVar) {
60
+ const root = localizeVar(rootVar);
61
+ if (!/_frag\d+$/.test(root)) return null;
62
+ const children = [];
63
+ const re = new RegExp(`^${root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.appendChild\\(([^)]+)\\);`);
64
+ for (const line of createLines) {
65
+ const m = localizeVar(line).match(re);
66
+ if (m) children.push(m[1]);
67
+ }
68
+ return children.length > 0 ? children : null;
69
+ }
70
+
53
71
  // ============================================================================
54
72
  // Prototype Installation
55
73
  // ============================================================================
56
74
 
57
- export function installComponentSupport(CodeGenerator) {
75
+ export function installComponentSupport(CodeGenerator, Lexer) {
76
+
77
+ // ==========================================================================
78
+ // Lexer: Render block rewriter
79
+ // ==========================================================================
80
+ // Transforms template syntax inside render blocks:
81
+ // - Implicit div for class-only selectors: .card → div.card
82
+ // - Combine #id selectors: div # main → div#main
83
+ // - Two-way binding: value <=> username → __bind_value__: username
84
+ // - Event modifiers: @click.prevent: → [@click.prevent]:
85
+ // - Dynamic classes: div.('card', x && 'active') → div.__clsx(...)
86
+ // - Implicit nesting: inject -> before INDENT for template elements
87
+ // - Hyphenated attributes: data-foo: "x" → "data-foo": "x"
88
+ // ==========================================================================
89
+
90
+ Lexer.prototype.rewriteRender = function() {
91
+ let gen = (tag, val, origin) => {
92
+ let t = [tag, val];
93
+ t.pre = 0;
94
+ t.data = null;
95
+ t.loc = origin?.loc ?? {r: 0, c: 0, n: 0};
96
+ t.spaced = false;
97
+ t.newLine = false;
98
+ t.generated = true;
99
+ if (origin) t.origin = origin;
100
+ return t;
101
+ };
102
+
103
+ let inRender = false;
104
+ let renderIndentLevel = 0;
105
+ let currentIndent = 0;
106
+ let pendingCallEnds = [];
107
+
108
+ let isHtmlTag = (name) => {
109
+ let tagPart = name.split('#')[0];
110
+ return TEMPLATE_TAGS.has(tagPart);
111
+ };
112
+
113
+ let isComponent = (name) => {
114
+ if (!name || typeof name !== 'string') return false;
115
+ return /^[A-Z]/.test(name);
116
+ };
117
+
118
+ let isTemplateTag = (name) => {
119
+ return isHtmlTag(name) || isComponent(name);
120
+ };
121
+
122
+ let startsWithTag = (tokens, i) => {
123
+ let j = i;
124
+ while (j > 0) {
125
+ let pt = tokens[j - 1][0];
126
+ if (pt === 'TERMINATOR' || pt === 'RENDER') {
127
+ break;
128
+ }
129
+ if (pt === 'INDENT' || pt === 'OUTDENT') {
130
+ let jt = tokens[j][0];
131
+ if (jt === 'CALL_END' || jt === ')') {
132
+ let open = jt === 'CALL_END' ? 'CALL_START' : '(';
133
+ let depth = 1;
134
+ let k = j - 1;
135
+ while (k >= 0 && depth > 0) {
136
+ let kt = tokens[k][0];
137
+ if (kt === jt) depth++;
138
+ else if (kt === open) depth--;
139
+ if (depth > 0) k--;
140
+ }
141
+ j = k;
142
+ continue;
143
+ }
144
+ break;
145
+ }
146
+ if (pt === 'CALL_END' || pt === ')') {
147
+ let open = pt === 'CALL_END' ? 'CALL_START' : '(';
148
+ let depth = 1;
149
+ let k = j - 2;
150
+ while (k >= 0 && depth > 0) {
151
+ let kt = tokens[k][0];
152
+ if (kt === 'CALL_END' || kt === ')') depth++;
153
+ else if (kt === 'CALL_START' || kt === '(') depth--;
154
+ if (depth > 0) k--;
155
+ }
156
+ j = k;
157
+ continue;
158
+ }
159
+ j--;
160
+ }
161
+ return tokens[j] && tokens[j][0] === 'IDENTIFIER' && isTemplateTag(tokens[j][1]);
162
+ };
163
+
164
+ this.scanTokens(function(token, i, tokens) {
165
+ let tag = token[0];
166
+ let nextToken = i < tokens.length - 1 ? tokens[i + 1] : null;
167
+
168
+ // Track entering render blocks
169
+ if (tag === 'RENDER') {
170
+ inRender = true;
171
+ renderIndentLevel = currentIndent + 1;
172
+ return 1;
173
+ }
174
+
175
+ // Track indentation
176
+ if (tag === 'INDENT') {
177
+ currentIndent++;
178
+ return 1;
179
+ }
180
+
181
+ if (tag === 'OUTDENT') {
182
+ currentIndent--;
183
+
184
+ // Insert pending CALL_END(s) after this OUTDENT
185
+ let inserted = 0;
186
+ while (pendingCallEnds.length > 0 && pendingCallEnds[pendingCallEnds.length - 1] > currentIndent) {
187
+ let callEndToken = gen('CALL_END', ')', token);
188
+ tokens.splice(i + 1 + inserted, 0, callEndToken);
189
+ pendingCallEnds.pop();
190
+ inserted++;
191
+ }
192
+
193
+ // Exit render block when we outdent past where it started
194
+ if (inRender && currentIndent < renderIndentLevel) {
195
+ inRender = false;
196
+ }
197
+ return 1 + inserted;
198
+ }
199
+
200
+ // Only process if we're inside a render block
201
+ if (!inRender) return 1;
202
+
203
+ // ─────────────────────────────────────────────────────────────────────
204
+ // Hyphenated attributes
205
+ // data-lucide: "search" → "data-lucide": "search"
206
+ // ─────────────────────────────────────────────────────────────────────
207
+ if (tag === 'IDENTIFIER' && !token.spaced) {
208
+ let parts = [token[1]];
209
+ let j = i + 1;
210
+ while (j + 1 < tokens.length) {
211
+ let hyphen = tokens[j];
212
+ let nextPart = tokens[j + 1];
213
+ if (hyphen[0] === '-' && !hyphen.spaced &&
214
+ (nextPart[0] === 'IDENTIFIER' || nextPart[0] === 'PROPERTY')) {
215
+ parts.push(nextPart[1]);
216
+ j += 2;
217
+ if (nextPart[0] === 'PROPERTY') break;
218
+ } else {
219
+ break;
220
+ }
221
+ }
222
+ if (parts.length > 1 && j > i + 1 && tokens[j - 1][0] === 'PROPERTY') {
223
+ token[0] = 'STRING';
224
+ token[1] = `"${parts.join('-')}"`;
225
+ tokens.splice(i + 1, j - i - 1);
226
+ return 1;
227
+ }
228
+ }
229
+
230
+ // ─────────────────────────────────────────────────────────────────────
231
+ // Implicit div for class-only or bare dot selectors
232
+ // .card → div.card | . (with children) → div
233
+ // ─────────────────────────────────────────────────────────────────────
234
+ if (tag === '.') {
235
+ let prevToken = i > 0 ? tokens[i - 1] : null;
236
+ let prevTag = prevToken ? prevToken[0] : null;
237
+ if (prevTag === 'INDENT' || prevTag === 'TERMINATOR') {
238
+ if (nextToken && nextToken[0] === 'PROPERTY') {
239
+ let divToken = gen('IDENTIFIER', 'div', token);
240
+ tokens.splice(i, 0, divToken);
241
+ return 2;
242
+ }
243
+ // Skip .('classes') — handled by dynamic classes handler below
244
+ if (!nextToken || nextToken[0] !== '(') {
245
+ token[0] = 'IDENTIFIER';
246
+ token[1] = 'div';
247
+ return 0;
248
+ }
249
+ }
250
+ }
251
+
252
+ // ─────────────────────────────────────────────────────────────────────
253
+ // Combine #id selectors
254
+ // div # main → div#main
255
+ // ─────────────────────────────────────────────────────────────────────
256
+ if (tag === 'IDENTIFIER' || tag === 'PROPERTY') {
257
+ let next = tokens[i + 1];
258
+ let nextNext = tokens[i + 2];
259
+ if (next && next[0] === '#' && nextNext && (nextNext[0] === 'PROPERTY' || nextNext[0] === 'IDENTIFIER')) {
260
+ token[1] = token[1] + '#' + nextNext[1];
261
+ if (nextNext.spaced) token.spaced = true;
262
+ tokens.splice(i + 1, 2);
263
+ return 1;
264
+ }
265
+ }
266
+
267
+ // ─────────────────────────────────────────────────────────────────────
268
+ // Two-way binding
269
+ // value <=> username → __bind_value__: username
270
+ // ─────────────────────────────────────────────────────────────────────
271
+ if (tag === 'BIND') {
272
+ let prevToken = i > 0 ? tokens[i - 1] : null;
273
+ let nextBindToken = tokens[i + 1];
274
+ if (prevToken && (prevToken[0] === 'IDENTIFIER' || prevToken[0] === 'PROPERTY') &&
275
+ nextBindToken && nextBindToken[0] === 'IDENTIFIER') {
276
+ prevToken[1] = `__bind_${prevToken[1]}__`;
277
+ token[0] = ':';
278
+ token[1] = ':';
279
+ return 1;
280
+ }
281
+ }
282
+
283
+ // ─────────────────────────────────────────────────────────────────────
284
+ // Event modifiers
285
+ // @click.prevent: handler → [@click.prevent]: handler
286
+ // ─────────────────────────────────────────────────────────────────────
287
+ if (tag === '@') {
288
+ let j = i + 1;
289
+ if (j < tokens.length && tokens[j][0] === 'PROPERTY') {
290
+ j++;
291
+ while (j + 1 < tokens.length && tokens[j][0] === '.' && tokens[j + 1][0] === 'PROPERTY') {
292
+ j += 2;
293
+ }
294
+ if (j > i + 2 && j < tokens.length && tokens[j][0] === ':') {
295
+ let openBracket = gen('[', '[', token);
296
+ tokens.splice(i, 0, openBracket);
297
+ let closeBracket = gen(']', ']', tokens[j + 1]);
298
+ tokens.splice(j + 1, 0, closeBracket);
299
+ return 2;
300
+ }
301
+ }
302
+ }
303
+
304
+ // ─────────────────────────────────────────────────────────────────────
305
+ // Dynamic classes
306
+ // div.('card', x && 'active') → div.__clsx('card', x && 'active')
307
+ // .('card') → div.__clsx('card')
308
+ // ─────────────────────────────────────────────────────────────────────
309
+ if (tag === '.' && nextToken && nextToken[0] === '(') {
310
+ let prevToken = i > 0 ? tokens[i - 1] : null;
311
+ let prevTag = prevToken ? prevToken[0] : null;
312
+ let atLineStart = prevTag === 'INDENT' || prevTag === 'TERMINATOR';
313
+
314
+ let cxToken = gen('PROPERTY', '__clsx', token);
315
+ nextToken[0] = 'CALL_START';
316
+ let depth = 1;
317
+ for (let j = i + 2; j < tokens.length && depth > 0; j++) {
318
+ if (tokens[j][0] === '(' || tokens[j][0] === 'CALL_START') depth++;
319
+ else if (tokens[j][0] === ')') {
320
+ depth--;
321
+ if (depth === 0) tokens[j][0] = 'CALL_END';
322
+ } else if (tokens[j][0] === 'CALL_END') depth--;
323
+ }
324
+
325
+ if (atLineStart) {
326
+ let divToken = gen('IDENTIFIER', 'div', token);
327
+ tokens.splice(i, 0, divToken);
328
+ tokens.splice(i + 2, 0, cxToken);
329
+ return 3;
330
+ } else {
331
+ tokens.splice(i + 1, 0, cxToken);
332
+ return 2;
333
+ }
334
+ }
335
+
336
+ // ─────────────────────────────────────────────────────────────────────
337
+ // Implicit nesting (inject -> before INDENT)
338
+ // ─────────────────────────────────────────────────────────────────────
339
+ if (nextToken && nextToken[0] === 'INDENT') {
340
+ if (tag === '->' || tag === '=>' || tag === 'CALL_START' || tag === '(') {
341
+ return 1;
342
+ }
343
+
344
+ let isTemplateElement = false;
345
+ let prevTag = i > 0 ? tokens[i - 1][0] : null;
346
+ let isAfterControlFlow = prevTag === 'IF' || prevTag === 'UNLESS' || prevTag === 'WHILE' || prevTag === 'UNTIL' || prevTag === 'WHEN';
347
+
348
+ // Detect __clsx CALL_END early — OUTDENT tokens inside multi-line .()
349
+ // args prevent startsWithTag from seeing the template tag, so we check
350
+ // for __clsx ownership first by counting balanced CALL_START/CALL_END.
351
+ let isClsxCallEnd = false;
352
+ if (tag === 'CALL_END') {
353
+ let depth = 1;
354
+ for (let j = i - 1; j >= 0 && depth > 0; j--) {
355
+ if (tokens[j][0] === 'CALL_END') depth++;
356
+ else if (tokens[j][0] === 'CALL_START') {
357
+ depth--;
358
+ if (depth === 0 && j > 0 && tokens[j - 1][0] === 'PROPERTY' && tokens[j - 1][1] === '__clsx') {
359
+ isClsxCallEnd = true;
360
+ }
361
+ }
362
+ }
363
+ }
364
+
365
+ if (isClsxCallEnd) {
366
+ isTemplateElement = true;
367
+ } else if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
368
+ isTemplateElement = true;
369
+ } else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'STRING_END' || tag === 'CALL_END' || tag === ')') {
370
+ isTemplateElement = startsWithTag(tokens, i);
371
+ }
372
+ else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
373
+ if (startsWithTag(tokens, i)) {
374
+ let commaToken = gen(',', ',', token);
375
+ let arrowToken = gen('->', '->', token);
376
+ arrowToken.newLine = true;
377
+ tokens.splice(i + 1, 0, commaToken, arrowToken);
378
+ return 3;
379
+ }
380
+ }
381
+
382
+ if (isTemplateElement) {
383
+ let isClassOrIdTail = tag === 'PROPERTY' && i > 0 && (tokens[i - 1][0] === '.' || tokens[i - 1][0] === '#');
384
+
385
+ if (isClsxCallEnd) {
386
+ let callStartToken = gen('CALL_START', '(', token);
387
+ let arrowToken = gen('->', '->', token);
388
+ arrowToken.newLine = true;
389
+ tokens.splice(i + 1, 0, callStartToken, arrowToken);
390
+ pendingCallEnds.push(currentIndent + 1);
391
+ return 3;
392
+ } else if ((tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail) {
393
+ // Bare tag or tag.class/tag#id (no other args): inject CALL_START -> and manage CALL_END
394
+ let callStartToken = gen('CALL_START', '(', token);
395
+ let arrowToken = gen('->', '->', token);
396
+ arrowToken.newLine = true;
397
+ tokens.splice(i + 1, 0, callStartToken, arrowToken);
398
+ pendingCallEnds.push(currentIndent + 1);
399
+ return 3;
400
+ } else {
401
+ // Tag with args: inject , -> (call wrapping handled by addImplicitBracesAndParens)
402
+ let commaToken = gen(',', ',', token);
403
+ let arrowToken = gen('->', '->', token);
404
+ arrowToken.newLine = true;
405
+ tokens.splice(i + 1, 0, commaToken, arrowToken);
406
+ return 3;
407
+ }
408
+ }
409
+ }
410
+
411
+ // ─────────────────────────────────────────────────────────────────────
412
+ // Bare component reference (PascalCase, no children, no args)
413
+ // Counter → Counter() so it gets treated as a component instantiation
414
+ // ─────────────────────────────────────────────────────────────────────
415
+ if (tag === 'IDENTIFIER' && isComponent(token[1]) &&
416
+ nextToken && (nextToken[0] === 'OUTDENT' || nextToken[0] === 'TERMINATOR')) {
417
+ tokens.splice(i + 1, 0, gen('CALL_START', '(', token), gen('CALL_END', ')', token));
418
+ return 3;
419
+ }
420
+
421
+ return 1;
422
+ });
423
+ };
424
+
425
+ // ==========================================================================
426
+ // CodeGenerator: Component compilation
427
+ // ==========================================================================
428
+
58
429
  const proto = CodeGenerator.prototype;
59
430
 
60
431
  // ==========================================================================
@@ -67,7 +438,7 @@ export function installComponentSupport(CodeGenerator) {
67
438
  */
68
439
  proto.localizeVar = function(line) {
69
440
  let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
70
- result = result.replace(/\bthis\./g, 'ctx.');
441
+ result = result.replace(/\bthis\b/g, 'ctx');
71
442
  return result;
72
443
  };
73
444
 
@@ -266,8 +637,14 @@ export function installComponentSupport(CodeGenerator) {
266
637
 
267
638
  // Computed (derived)
268
639
  for (const { name, expr } of derivedVars) {
269
- const val = this.generateInComponent(expr, 'value');
270
- lines.push(` this.${name} = __computed(() => ${val});`);
640
+ if (this.is(expr, 'block') && expr.length > 2) {
641
+ const transformed = this.transformComponentMembers(expr);
642
+ const body = this.generateFunctionBody(transformed);
643
+ lines.push(` this.${name} = __computed(() => ${body});`);
644
+ } else {
645
+ const val = this.generateInComponent(expr, 'value');
646
+ lines.push(` this.${name} = __computed(() => ${val});`);
647
+ }
271
648
  }
272
649
 
273
650
  // Effects
@@ -643,10 +1020,10 @@ export function installComponentSupport(CodeGenerator) {
643
1020
  const eventName = key[2];
644
1021
  // Bind method references to this
645
1022
  if (typeof value === 'string' && this.componentMembers?.has(value)) {
646
- this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => this.${value}(e));`);
1023
+ this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => this.${value}(e)));`);
647
1024
  } else {
648
1025
  const handlerCode = this.generateInComponent(value, 'value');
649
- this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
1026
+ this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => (${handlerCode})(e)));`);
650
1027
  }
651
1028
  continue;
652
1029
  }
@@ -860,7 +1237,7 @@ export function installComponentSupport(CodeGenerator) {
860
1237
  /__effect\(\(\) => \{/g,
861
1238
  'disposers.push(__effect(() => {'
862
1239
  ).replace(
863
- /\}\);$/g,
1240
+ /\}\);$/gm,
864
1241
  '}));'
865
1242
  );
866
1243
  factoryLines.push(` ${wrappedLine}`);
@@ -873,7 +1250,14 @@ export function installComponentSupport(CodeGenerator) {
873
1250
  if (hasEffects) {
874
1251
  factoryLines.push(` disposers.forEach(d => d());`);
875
1252
  }
876
- factoryLines.push(` if (detaching) ${localizeVar(rootVar)}.remove();`);
1253
+ const condFragChildren = getFragChildren(rootVar, createLines, localizeVar);
1254
+ if (condFragChildren) {
1255
+ for (const child of condFragChildren) {
1256
+ factoryLines.push(` if (detaching) ${child}.remove();`);
1257
+ }
1258
+ } else {
1259
+ factoryLines.push(` if (detaching) ${localizeVar(rootVar)}.remove();`);
1260
+ }
877
1261
  factoryLines.push(` }`);
878
1262
 
879
1263
  factoryLines.push(` };`);
@@ -963,9 +1347,16 @@ export function installComponentSupport(CodeGenerator) {
963
1347
  }
964
1348
  factoryLines.push(` },`);
965
1349
 
966
- // m() - mount
1350
+ // m() - mount (also repositions already-mounted blocks)
1351
+ const loopFragChildren = getFragChildren(itemNode, itemCreateLines, localizeVar);
967
1352
  factoryLines.push(` m(target, anchor) {`);
968
- factoryLines.push(` target.insertBefore(${localizeVar(itemNode)}, anchor);`);
1353
+ if (loopFragChildren) {
1354
+ for (const child of loopFragChildren) {
1355
+ factoryLines.push(` target.insertBefore(${child}, anchor);`);
1356
+ }
1357
+ } else {
1358
+ factoryLines.push(` target.insertBefore(${localizeVar(itemNode)}, anchor);`);
1359
+ }
969
1360
  factoryLines.push(` },`);
970
1361
 
971
1362
  // p() - update
@@ -979,7 +1370,7 @@ export function installComponentSupport(CodeGenerator) {
979
1370
  /__effect\(\(\) => \{/g,
980
1371
  'disposers.push(__effect(() => {'
981
1372
  ).replace(
982
- /\}\);$/g,
1373
+ /\}\);$/gm,
983
1374
  '}));'
984
1375
  );
985
1376
  factoryLines.push(` ${wrappedLine}`);
@@ -992,7 +1383,13 @@ export function installComponentSupport(CodeGenerator) {
992
1383
  if (hasEffects) {
993
1384
  factoryLines.push(` disposers.forEach(d => d());`);
994
1385
  }
995
- factoryLines.push(` if (detaching) ${localizeVar(itemNode)}.remove();`);
1386
+ if (loopFragChildren) {
1387
+ for (const child of loopFragChildren) {
1388
+ factoryLines.push(` if (detaching) ${child}.remove();`);
1389
+ }
1390
+ } else {
1391
+ factoryLines.push(` if (detaching) ${localizeVar(itemNode)}.remove();`);
1392
+ }
996
1393
  factoryLines.push(` }`);
997
1394
 
998
1395
  factoryLines.push(` };`);
@@ -1015,14 +1412,12 @@ export function installComponentSupport(CodeGenerator) {
1015
1412
  setupLines.push(` const ${itemVar} = items[${indexVar}];`);
1016
1413
  setupLines.push(` const key = ${keyExpr};`);
1017
1414
  setupLines.push(` let block = map.get(key);`);
1018
- setupLines.push(` if (block) {`);
1019
- setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
1020
- setupLines.push(` } else {`);
1415
+ setupLines.push(` if (!block) {`);
1021
1416
  setupLines.push(` block = ${blockName}(this, ${itemVar}, ${indexVar});`);
1022
1417
  setupLines.push(` block.c();`);
1023
- setupLines.push(` block.m(parent, anchor);`);
1024
- setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
1025
1418
  setupLines.push(` }`);
1419
+ setupLines.push(` block.m(parent, anchor);`);
1420
+ setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
1026
1421
  setupLines.push(` newMap.set(key, block);`);
1027
1422
  setupLines.push(` }`);
1028
1423
  setupLines.push(``);
@@ -197,7 +197,7 @@ grammar =
197
197
  ComputedAssign: [
198
198
  o 'Assignable COMPUTED_ASSIGN Expression' , '["computed", 1, 3]'
199
199
  o 'Assignable COMPUTED_ASSIGN TERMINATOR Expression' , '["computed", 1, 4]'
200
- o 'Assignable COMPUTED_ASSIGN INDENT Expression OUTDENT', '["computed", 1, 4]'
200
+ o 'Assignable COMPUTED_ASSIGN Block' , '["computed", 1, 3]'
201
201
  ]
202
202
 
203
203
  # Reactive readonly (=!) — constants that cannot be reassigned
@@ -443,7 +443,7 @@ grammar =
443
443
  Invocation: [
444
444
  o 'Value String' , '["tagged-template", 1, 2]' # Tagged template
445
445
  o 'Value Arguments' , '[1, ...2]' # Regular call
446
- o 'Value ES6_OPTIONAL_CALL Arguments' , '["optcall", 1, ...3]' # Optional call: x?.(args)
446
+ o 'Value ES6_OPTIONAL_CALL Arguments' , '["optcall", 1, ...3]' # Optional call: x?.(args)
447
447
  o 'SUPER Arguments' , '["super", ...2]' # Super call
448
448
  o 'DYNAMIC_IMPORT Arguments' , '[1, ...2]' # Dynamic import()
449
449
  ]