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/CHANGELOG.md +48 -0
- package/README.md +14 -14
- package/docs/RIP-LANG.md +10 -1
- package/docs/dist/rip-ui.min.js +169 -169
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +163 -163
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/example/index.html +34 -0
- package/docs/example/index.json +14 -0
- package/docs/index.html +1475 -3
- package/docs/sierpinski.html +114 -0
- package/package.json +3 -3
- package/src/browser.js +18 -9
- package/src/compiler.js +4 -1
- package/src/components.js +415 -20
- package/src/grammar/grammar.rip +2 -2
- package/src/grammar/parser.js +360 -0
- package/src/lexer.js +12 -275
- package/src/parser.js +5 -6
- package/docs/demo.html +0 -342
- package/docs/dist/rip.browser.js +0 -8023
- package/docs/dist/ui.js +0 -962
- package/docs/dist/ui.min.js +0 -2
- package/docs/dist/ui.min.js.br +0 -0
- package/docs/dist/ui.rip +0 -957
- package/docs/dist/ui.rip.br +0 -0
- package/docs/playground-app.html +0 -1022
- package/docs/playground-js.html +0 -1645
- package/docs/playground-rip-ui.html +0 -1419
- package/docs/playground-rip.html +0 -1450
- package/src/parser-rd.js +0 -3242
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
|
// ==========================================================================
|
|
@@ -759,6 +758,16 @@ export class Lexer {
|
|
|
759
758
|
if (this.inRenderBlock && LINE_CONTINUER_RE.test(this.chunk) && /^\s*\./.test(this.chunk)) {
|
|
760
759
|
return false;
|
|
761
760
|
}
|
|
761
|
+
// Inside render blocks, a bare . at line start is div shorthand, not continuation
|
|
762
|
+
if (this.inRenderBlock && this.prevTag() === '.') {
|
|
763
|
+
let len = this.tokens.length;
|
|
764
|
+
if (len >= 2) {
|
|
765
|
+
let beforeDot = this.tokens[len - 2][0];
|
|
766
|
+
if (beforeDot === 'INDENT' || beforeDot === 'TERMINATOR' || beforeDot === 'OUTDENT') {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
762
771
|
return LINE_CONTINUER_RE.test(this.chunk) || UNFINISHED.has(this.prevTag());
|
|
763
772
|
}
|
|
764
773
|
|
|
@@ -1263,7 +1272,7 @@ export class Lexer {
|
|
|
1263
1272
|
}
|
|
1264
1273
|
|
|
1265
1274
|
// ==========================================================================
|
|
1266
|
-
// Rewriter
|
|
1275
|
+
// Rewriter
|
|
1267
1276
|
// ==========================================================================
|
|
1268
1277
|
|
|
1269
1278
|
rewrite(tokens) {
|
|
@@ -1272,7 +1281,7 @@ export class Lexer {
|
|
|
1272
1281
|
this.closeOpenCalls();
|
|
1273
1282
|
this.closeOpenIndexes();
|
|
1274
1283
|
this.normalizeLines();
|
|
1275
|
-
this.rewriteRender();
|
|
1284
|
+
this.rewriteRender?.();
|
|
1276
1285
|
this.rewriteTypes();
|
|
1277
1286
|
this.tagPostfixConditionals();
|
|
1278
1287
|
this.addImplicitBracesAndParens();
|
|
@@ -1383,278 +1392,6 @@ export class Lexer {
|
|
|
1383
1392
|
});
|
|
1384
1393
|
}
|
|
1385
1394
|
|
|
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
1395
|
tagPostfixConditionals() {
|
|
1659
1396
|
let original = null;
|
|
1660
1397
|
|