rip-lang 3.8.9 → 3.9.0

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/lexer.js CHANGED
@@ -39,7 +39,6 @@
39
39
  //
40
40
  // ==========================================================================
41
41
 
42
- import { TEMPLATE_TAGS } from './tags.js';
43
42
  import { installTypeSupport } from './types.js';
44
43
 
45
44
  // ==========================================================================
@@ -1263,7 +1262,7 @@ export class Lexer {
1263
1262
  }
1264
1263
 
1265
1264
  // ==========================================================================
1266
- // Rewriter — 8 passes
1265
+ // Rewriter
1267
1266
  // ==========================================================================
1268
1267
 
1269
1268
  rewrite(tokens) {
@@ -1272,7 +1271,7 @@ export class Lexer {
1272
1271
  this.closeOpenCalls();
1273
1272
  this.closeOpenIndexes();
1274
1273
  this.normalizeLines();
1275
- this.rewriteRender();
1274
+ this.rewriteRender?.();
1276
1275
  this.rewriteTypes();
1277
1276
  this.tagPostfixConditionals();
1278
1277
  this.addImplicitBracesAndParens();
@@ -1383,278 +1382,6 @@ export class Lexer {
1383
1382
  });
1384
1383
  }
1385
1384
 
1386
- // =========================================================================
1387
- // Render block rewriter
1388
- // =========================================================================
1389
- // Transforms template syntax inside render blocks:
1390
- // - Implicit div for class-only selectors: .card → div.card
1391
- // - Combine #id selectors: div # main → div#main
1392
- // - Two-way binding: value <=> username → __bind_value__: username
1393
- // - Event modifiers: @click.prevent: → [@click.prevent]:
1394
- // - Dynamic classes: div.('card', x && 'active') → div.__clsx(...)
1395
- // - Implicit nesting: inject -> before INDENT for template elements
1396
- // - Hyphenated attributes: data-foo: "x" → "data-foo": "x"
1397
- // =========================================================================
1398
- rewriteRender() {
1399
- let inRender = false;
1400
- let renderIndentLevel = 0;
1401
- let currentIndent = 0;
1402
- let pendingCallEnds = [];
1403
-
1404
- let isHtmlTag = (name) => {
1405
- let tagPart = name.split('#')[0];
1406
- return TEMPLATE_TAGS.has(tagPart);
1407
- };
1408
-
1409
- let isComponent = (name) => {
1410
- if (!name || typeof name !== 'string') return false;
1411
- return /^[A-Z]/.test(name);
1412
- };
1413
-
1414
- let isTemplateTag = (name) => {
1415
- return isHtmlTag(name) || isComponent(name);
1416
- };
1417
-
1418
- let startsWithTag = (tokens, i) => {
1419
- let j = i;
1420
- while (j > 0) {
1421
- let pt = tokens[j - 1][0];
1422
- if (pt === 'INDENT' || pt === 'OUTDENT' || pt === 'TERMINATOR' || pt === 'RENDER' || pt === 'CALL_END' || pt === ')') {
1423
- break;
1424
- }
1425
- j--;
1426
- }
1427
- return tokens[j] && tokens[j][0] === 'IDENTIFIER' && isTemplateTag(tokens[j][1]);
1428
- };
1429
-
1430
- this.scanTokens(function(token, i, tokens) {
1431
- let tag = token[0];
1432
- let nextToken = i < tokens.length - 1 ? tokens[i + 1] : null;
1433
-
1434
- // Track entering render blocks
1435
- if (tag === 'RENDER') {
1436
- inRender = true;
1437
- renderIndentLevel = currentIndent + 1;
1438
- return 1;
1439
- }
1440
-
1441
- // Track indentation
1442
- if (tag === 'INDENT') {
1443
- currentIndent++;
1444
- return 1;
1445
- }
1446
-
1447
- if (tag === 'OUTDENT') {
1448
- currentIndent--;
1449
-
1450
- // Insert pending CALL_END(s) after this OUTDENT
1451
- let inserted = 0;
1452
- while (pendingCallEnds.length > 0 && pendingCallEnds[pendingCallEnds.length - 1] > currentIndent) {
1453
- let callEndToken = gen('CALL_END', ')', token);
1454
- tokens.splice(i + 1 + inserted, 0, callEndToken);
1455
- pendingCallEnds.pop();
1456
- inserted++;
1457
- }
1458
-
1459
- // Exit render block when we outdent past where it started
1460
- if (inRender && currentIndent < renderIndentLevel) {
1461
- inRender = false;
1462
- }
1463
- return 1 + inserted;
1464
- }
1465
-
1466
- // Only process if we're inside a render block
1467
- if (!inRender) return 1;
1468
-
1469
- // ─────────────────────────────────────────────────────────────────────
1470
- // Hyphenated attributes
1471
- // data-lucide: "search" → "data-lucide": "search"
1472
- // ─────────────────────────────────────────────────────────────────────
1473
- if (tag === 'IDENTIFIER' && !token.spaced) {
1474
- let parts = [token[1]];
1475
- let j = i + 1;
1476
- while (j + 1 < tokens.length) {
1477
- let hyphen = tokens[j];
1478
- let nextPart = tokens[j + 1];
1479
- if (hyphen[0] === '-' && !hyphen.spaced &&
1480
- (nextPart[0] === 'IDENTIFIER' || nextPart[0] === 'PROPERTY')) {
1481
- parts.push(nextPart[1]);
1482
- j += 2;
1483
- if (nextPart[0] === 'PROPERTY') break;
1484
- } else {
1485
- break;
1486
- }
1487
- }
1488
- if (parts.length > 1 && j > i + 1 && tokens[j - 1][0] === 'PROPERTY') {
1489
- token[0] = 'STRING';
1490
- token[1] = `"${parts.join('-')}"`;
1491
- tokens.splice(i + 1, j - i - 1);
1492
- return 1;
1493
- }
1494
- }
1495
-
1496
- // ─────────────────────────────────────────────────────────────────────
1497
- // Implicit div for class-only selectors
1498
- // .card → div.card
1499
- // ─────────────────────────────────────────────────────────────────────
1500
- if (tag === '.') {
1501
- let prevToken = i > 0 ? tokens[i - 1] : null;
1502
- let prevTag = prevToken ? prevToken[0] : null;
1503
- if (prevTag === 'INDENT' || prevTag === 'TERMINATOR') {
1504
- if (nextToken && nextToken[0] === 'PROPERTY') {
1505
- let divToken = gen('IDENTIFIER', 'div', token);
1506
- tokens.splice(i, 0, divToken);
1507
- return 2;
1508
- }
1509
- }
1510
- }
1511
-
1512
- // ─────────────────────────────────────────────────────────────────────
1513
- // Combine #id selectors
1514
- // div # main → div#main
1515
- // ─────────────────────────────────────────────────────────────────────
1516
- if (tag === 'IDENTIFIER' || tag === 'PROPERTY') {
1517
- let next = tokens[i + 1];
1518
- let nextNext = tokens[i + 2];
1519
- if (next && next[0] === '#' && nextNext && (nextNext[0] === 'PROPERTY' || nextNext[0] === 'IDENTIFIER')) {
1520
- token[1] = token[1] + '#' + nextNext[1];
1521
- if (nextNext.spaced) token.spaced = true;
1522
- tokens.splice(i + 1, 2);
1523
- return 1;
1524
- }
1525
- }
1526
-
1527
- // ─────────────────────────────────────────────────────────────────────
1528
- // Two-way binding
1529
- // value <=> username → __bind_value__: username
1530
- // ─────────────────────────────────────────────────────────────────────
1531
- if (tag === 'BIND') {
1532
- let prevToken = i > 0 ? tokens[i - 1] : null;
1533
- let nextBindToken = tokens[i + 1];
1534
- if (prevToken && (prevToken[0] === 'IDENTIFIER' || prevToken[0] === 'PROPERTY') &&
1535
- nextBindToken && nextBindToken[0] === 'IDENTIFIER') {
1536
- prevToken[1] = `__bind_${prevToken[1]}__`;
1537
- token[0] = ':';
1538
- token[1] = ':';
1539
- return 1;
1540
- }
1541
- }
1542
-
1543
- // ─────────────────────────────────────────────────────────────────────
1544
- // Event modifiers
1545
- // @click.prevent: handler → [@click.prevent]: handler
1546
- // ─────────────────────────────────────────────────────────────────────
1547
- if (tag === '@') {
1548
- let j = i + 1;
1549
- if (j < tokens.length && tokens[j][0] === 'PROPERTY') {
1550
- j++;
1551
- while (j + 1 < tokens.length && tokens[j][0] === '.' && tokens[j + 1][0] === 'PROPERTY') {
1552
- j += 2;
1553
- }
1554
- if (j > i + 2 && j < tokens.length && tokens[j][0] === ':') {
1555
- let openBracket = gen('[', '[', token);
1556
- tokens.splice(i, 0, openBracket);
1557
- let closeBracket = gen(']', ']', tokens[j + 1]);
1558
- tokens.splice(j + 1, 0, closeBracket);
1559
- return 2;
1560
- }
1561
- }
1562
- }
1563
-
1564
- // ─────────────────────────────────────────────────────────────────────
1565
- // Dynamic classes
1566
- // div.('card', x && 'active') → div.__clsx('card', x && 'active')
1567
- // .('card') → div.__clsx('card')
1568
- // ─────────────────────────────────────────────────────────────────────
1569
- if (tag === '.' && nextToken && nextToken[0] === '(') {
1570
- let prevToken = i > 0 ? tokens[i - 1] : null;
1571
- let prevTag = prevToken ? prevToken[0] : null;
1572
- let atLineStart = prevTag === 'INDENT' || prevTag === 'TERMINATOR';
1573
-
1574
- let cxToken = gen('PROPERTY', '__clsx', token);
1575
- nextToken[0] = 'CALL_START';
1576
- let depth = 1;
1577
- for (let j = i + 2; j < tokens.length && depth > 0; j++) {
1578
- if (tokens[j][0] === '(' || tokens[j][0] === 'CALL_START') depth++;
1579
- else if (tokens[j][0] === ')') {
1580
- depth--;
1581
- if (depth === 0) tokens[j][0] = 'CALL_END';
1582
- } else if (tokens[j][0] === 'CALL_END') depth--;
1583
- }
1584
-
1585
- if (atLineStart) {
1586
- let divToken = gen('IDENTIFIER', 'div', token);
1587
- tokens.splice(i, 0, divToken);
1588
- tokens.splice(i + 2, 0, cxToken);
1589
- return 3;
1590
- } else {
1591
- tokens.splice(i + 1, 0, cxToken);
1592
- return 2;
1593
- }
1594
- }
1595
-
1596
- // ─────────────────────────────────────────────────────────────────────
1597
- // Implicit nesting (inject -> before INDENT)
1598
- // ─────────────────────────────────────────────────────────────────────
1599
- if (nextToken && nextToken[0] === 'INDENT') {
1600
- if (tag === '->' || tag === '=>' || tag === 'CALL_START' || tag === '(') {
1601
- return 1;
1602
- }
1603
-
1604
- let isTemplateElement = false;
1605
- let prevTag = i > 0 ? tokens[i - 1][0] : null;
1606
- let isAfterControlFlow = prevTag === 'IF' || prevTag === 'UNLESS' || prevTag === 'WHILE' || prevTag === 'UNTIL' || prevTag === 'WHEN';
1607
-
1608
- if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
1609
- isTemplateElement = true;
1610
- } else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'CALL_END' || tag === ')') {
1611
- isTemplateElement = startsWithTag(tokens, i);
1612
- }
1613
- else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
1614
- if (startsWithTag(tokens, i)) {
1615
- let commaToken = gen(',', ',', token);
1616
- let arrowToken = gen('->', '->', token);
1617
- arrowToken.newLine = true;
1618
- tokens.splice(i + 1, 0, commaToken, arrowToken);
1619
- return 3;
1620
- }
1621
- }
1622
-
1623
- if (isTemplateElement) {
1624
- let isClassOrIdTail = tag === 'PROPERTY' && i > 0 && (tokens[i - 1][0] === '.' || tokens[i - 1][0] === '#');
1625
- if ((tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail) {
1626
- // Bare tag or tag.class/tag#id (no other args): inject CALL_START -> and manage CALL_END
1627
- let callStartToken = gen('CALL_START', '(', token);
1628
- let arrowToken = gen('->', '->', token);
1629
- arrowToken.newLine = true;
1630
- tokens.splice(i + 1, 0, callStartToken, arrowToken);
1631
- pendingCallEnds.push(currentIndent + 1);
1632
- return 3;
1633
- } else {
1634
- // Tag with args: inject , -> (call wrapping handled by addImplicitBracesAndParens)
1635
- let commaToken = gen(',', ',', token);
1636
- let arrowToken = gen('->', '->', token);
1637
- arrowToken.newLine = true;
1638
- tokens.splice(i + 1, 0, commaToken, arrowToken);
1639
- return 3;
1640
- }
1641
- }
1642
- }
1643
-
1644
- // ─────────────────────────────────────────────────────────────────────
1645
- // Bare component reference (PascalCase, no children, no args)
1646
- // Counter → Counter() so it gets treated as a component instantiation
1647
- // ─────────────────────────────────────────────────────────────────────
1648
- if (tag === 'IDENTIFIER' && isComponent(token[1]) &&
1649
- nextToken && (nextToken[0] === 'OUTDENT' || nextToken[0] === 'TERMINATOR')) {
1650
- tokens.splice(i + 1, 0, gen('CALL_START', '(', token), gen('CALL_END', ')', token));
1651
- return 3;
1652
- }
1653
-
1654
- return 1;
1655
- });
1656
- }
1657
-
1658
1385
  tagPostfixConditionals() {
1659
1386
  let original = null;
1660
1387