rip-lang 3.15.4 → 3.16.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/README.md +6 -4
- package/bin/rip +167 -12
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-APP.md +808 -0
- package/docs/RIP-DUCKDB.md +477 -0
- package/docs/RIP-INTRO.md +396 -0
- package/docs/RIP-LANG.md +59 -5
- package/docs/RIP-SCHEMA.md +191 -8
- package/docs/RIP-TYPES.md +74 -103
- package/docs/demo/README.md +4 -3
- package/docs/dist/rip.js +3627 -1470
- package/docs/dist/rip.min.js +671 -244
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +7 -7
- package/docs/example/index.json.br +0 -0
- package/docs/extensions/duckdb/manifest.json +1 -1
- package/docs/extensions/duckdb/v1.5.2/linux_amd64/ripdb.duckdb_extension.gz +0 -0
- package/docs/extensions/duckdb/v1.5.2/osx_arm64/ripdb.duckdb_extension.gz +0 -0
- package/docs/extensions/vscode/print/index.html +2 -1
- package/docs/extensions/vscode/print/print-1.0.13.vsix +0 -0
- package/docs/extensions/vscode/print/print-1.0.14.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-0.5.15.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/ui/bundle.json +61 -0
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/hljs-rip.js +0 -7
- package/docs/ui/index.css +66 -23
- package/docs/ui/index.html +6 -6
- package/package.json +9 -3
- package/rip-loader.js +64 -2
- package/src/AGENTS.md +63 -36
- package/src/browser.js +96 -14
- package/src/compiler.js +960 -143
- package/src/components.js +794 -88
- package/src/{types-emit.js → dts.js} +181 -71
- package/src/grammar/README.md +1 -1
- package/src/grammar/grammar.rip +111 -97
- package/src/lexer.js +132 -18
- package/src/parser.js +203 -205
- package/src/repl.js +74 -6
- package/src/schema/runtime-orm.js +168 -4
- package/src/schema/runtime-validate.js +146 -2
- package/src/schema/runtime.generated.js +314 -6
- package/src/schema/schema.js +5 -5
- package/src/sourcemaps.js +277 -1
- package/src/stdlib.js +253 -0
- package/src/typecheck.js +2023 -106
- package/src/types.js +127 -7
- package/docs/ui/accordion.rip +0 -103
- package/docs/ui/alert-dialog.rip +0 -53
- package/docs/ui/autocomplete.rip +0 -115
- package/docs/ui/avatar.rip +0 -37
- package/docs/ui/badge.rip +0 -15
- package/docs/ui/breadcrumb.rip +0 -47
- package/docs/ui/button-group.rip +0 -26
- package/docs/ui/button.rip +0 -23
- package/docs/ui/card.rip +0 -25
- package/docs/ui/carousel.rip +0 -110
- package/docs/ui/checkbox-group.rip +0 -61
- package/docs/ui/checkbox.rip +0 -33
- package/docs/ui/collapsible.rip +0 -50
- package/docs/ui/combobox.rip +0 -130
- package/docs/ui/context-menu.rip +0 -88
- package/docs/ui/date-picker.rip +0 -206
- package/docs/ui/dialog.rip +0 -60
- package/docs/ui/drawer.rip +0 -58
- package/docs/ui/editable-value.rip +0 -82
- package/docs/ui/field.rip +0 -53
- package/docs/ui/fieldset.rip +0 -22
- package/docs/ui/form.rip +0 -39
- package/docs/ui/grid.rip +0 -901
- package/docs/ui/input-group.rip +0 -28
- package/docs/ui/input.rip +0 -36
- package/docs/ui/label.rip +0 -16
- package/docs/ui/menu.rip +0 -134
- package/docs/ui/menubar.rip +0 -151
- package/docs/ui/meter.rip +0 -36
- package/docs/ui/multi-select.rip +0 -203
- package/docs/ui/native-select.rip +0 -33
- package/docs/ui/nav-menu.rip +0 -126
- package/docs/ui/number-field.rip +0 -162
- package/docs/ui/otp-field.rip +0 -89
- package/docs/ui/pagination.rip +0 -123
- package/docs/ui/popover.rip +0 -93
- package/docs/ui/preview-card.rip +0 -75
- package/docs/ui/progress.rip +0 -25
- package/docs/ui/radio-group.rip +0 -57
- package/docs/ui/resizable.rip +0 -123
- package/docs/ui/scroll-area.rip +0 -145
- package/docs/ui/select.rip +0 -151
- package/docs/ui/separator.rip +0 -17
- package/docs/ui/skeleton.rip +0 -22
- package/docs/ui/slider.rip +0 -165
- package/docs/ui/spinner.rip +0 -17
- package/docs/ui/table.rip +0 -27
- package/docs/ui/tabs.rip +0 -113
- package/docs/ui/textarea.rip +0 -48
- package/docs/ui/toast.rip +0 -87
- package/docs/ui/toggle-group.rip +0 -71
- package/docs/ui/toggle.rip +0 -24
- package/docs/ui/toolbar.rip +0 -38
- package/docs/ui/tooltip.rip +0 -85
- package/src/app.rip +0 -1571
- package/src/sourcemap-merge.js +0 -287
- /package/docs/demo/{components → routes}/_layout.rip +0 -0
- /package/docs/demo/{components → routes}/about.rip +0 -0
- /package/docs/demo/{components → routes}/card.rip +0 -0
- /package/docs/demo/{components → routes}/counter.rip +0 -0
- /package/docs/demo/{components → routes}/index.rip +0 -0
- /package/docs/demo/{components → routes}/todos.rip +0 -0
- /package/src/schema/{dts-emit.js → dts.js} +0 -0
package/docs/ui/index.css
CHANGED
|
@@ -130,6 +130,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
130
130
|
&[data-selected] { font-weight: 600; color: #18181b; }
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
.gallery [role="status"][data-empty] {
|
|
134
|
+
display: block; padding: 8px 12px; border-radius: 4px;
|
|
135
|
+
font-size: 14px; line-height: 21px; color: #71717a;
|
|
136
|
+
}
|
|
137
|
+
|
|
133
138
|
/* ── Combobox clear button ── */
|
|
134
139
|
|
|
135
140
|
.gallery [data-clear] {
|
|
@@ -260,8 +265,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
260
265
|
display: inline-flex; align-items: center; gap: 8px;
|
|
261
266
|
padding: 6px 12px; border: 1px solid #d4d4d8; border-radius: 12px;
|
|
262
267
|
background: white; cursor: pointer; font-size: 14px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.gallery [role="checkbox"][data-checked] {
|
|
271
|
+
background: #fafafa; border-color: #18181b; color: #18181b;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.gallery [role="switch"] {
|
|
275
|
+
border-radius: 999px;
|
|
276
|
+
}
|
|
263
277
|
|
|
264
|
-
|
|
278
|
+
.gallery [role="switch"][data-checked] {
|
|
279
|
+
background: #dbeafe; border-color: #60a5fa; color: #1d4ed8;
|
|
265
280
|
}
|
|
266
281
|
|
|
267
282
|
/* ── Menu ── */
|
|
@@ -612,26 +627,32 @@ select.rip-grid-editor { cursor: pointer; padding-left: 4px; }
|
|
|
612
627
|
/* ── MultiSelect ── */
|
|
613
628
|
|
|
614
629
|
.gallery [data-chips] {
|
|
615
|
-
display: flex; flex-wrap: wrap; align-items: center; gap:
|
|
616
|
-
padding:
|
|
617
|
-
background: white; min-height:
|
|
630
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
|
631
|
+
padding: 6px 12px; border: 1px solid #d4d4d8; border-radius: 16px;
|
|
632
|
+
background: white; min-height: 42px; cursor: text;
|
|
618
633
|
|
|
619
634
|
&:focus-within { border-color: #18181b; box-shadow: 0 0 0 2px rgba(59,130,246,0.15); }
|
|
620
635
|
|
|
621
|
-
input
|
|
622
|
-
|
|
623
|
-
|
|
636
|
+
input,
|
|
637
|
+
input[type="text"] {
|
|
638
|
+
appearance: none; -webkit-appearance: none;
|
|
639
|
+
border: none !important; outline: none; box-shadow: none !important; border-radius: 0 !important;
|
|
640
|
+
font: inherit; font-size: 14px; line-height: 1.2;
|
|
641
|
+
min-width: 88px; margin: 0;
|
|
642
|
+
flex: 1 1 120px; height: 28px; min-height: 28px; padding: 0 2px !important;
|
|
643
|
+
background: transparent !important;
|
|
624
644
|
}
|
|
625
645
|
}
|
|
626
646
|
|
|
627
647
|
.gallery [data-chip] {
|
|
628
648
|
display: inline-flex; align-items: center; gap: 4px;
|
|
629
|
-
padding:
|
|
630
|
-
border-radius:
|
|
649
|
+
min-height: 28px; padding: 0 10px; background: #fafafa; color: #18181b;
|
|
650
|
+
border: 1px solid #e4e4e7; border-radius: 10px; font-size: 12px; font-weight: 500;
|
|
631
651
|
|
|
632
652
|
[data-remove] {
|
|
633
653
|
border: none; background: none; color: #18181b; cursor: pointer;
|
|
634
|
-
font-size: 11px; padding: 0
|
|
654
|
+
width: 16px; height: 16px; font-size: 11px; padding: 0; line-height: 1; border-radius: 999px;
|
|
655
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
635
656
|
|
|
636
657
|
&:hover { background: #f4f4f5; }
|
|
637
658
|
}
|
|
@@ -767,6 +788,11 @@ select.rip-grid-editor { cursor: pointer; padding-left: 4px; }
|
|
|
767
788
|
animation: fadeIn 0.15s ease;
|
|
768
789
|
}
|
|
769
790
|
|
|
791
|
+
.gallery #preview-card [data-content] {
|
|
792
|
+
padding: 0; margin: 0; border: none; border-radius: 0;
|
|
793
|
+
background: transparent; box-shadow: none; min-width: 0;
|
|
794
|
+
}
|
|
795
|
+
|
|
770
796
|
/* ── Menubar ── */
|
|
771
797
|
|
|
772
798
|
.gallery [role="menubar"] {
|
|
@@ -946,7 +972,7 @@ html { scroll-behavior: auto; }
|
|
|
946
972
|
html.no-transition, html.no-transition * { transition: none !important; }
|
|
947
973
|
|
|
948
974
|
.toc {
|
|
949
|
-
display: grid; grid-template-columns:
|
|
975
|
+
display: grid; grid-template-columns: 58% 42%;
|
|
950
976
|
margin-bottom: 36px;
|
|
951
977
|
background: white; border: 1px solid #e4e4e7; border-radius: 12px;
|
|
952
978
|
overflow: hidden;
|
|
@@ -954,14 +980,14 @@ html.no-transition, html.no-transition * { transition: none !important; }
|
|
|
954
980
|
|
|
955
981
|
.toc-nav {
|
|
956
982
|
display: flex; flex-wrap: wrap; align-content: flex-start;
|
|
957
|
-
gap:
|
|
983
|
+
gap: 14px 24px; padding: 18px 22px;
|
|
958
984
|
}
|
|
959
985
|
|
|
960
|
-
.toc-group { display: flex; flex-direction: column; gap:
|
|
986
|
+
.toc-group { display: flex; flex-direction: column; align-items: flex-start; gap: 7px; }
|
|
961
987
|
|
|
962
988
|
.toc-label {
|
|
963
989
|
font-size: 10px; font-weight: 700; letter-spacing: 0.08em;
|
|
964
|
-
text-transform: uppercase; color: #71717a; margin-bottom:
|
|
990
|
+
text-transform: uppercase; color: #71717a; margin-bottom: 3px;
|
|
965
991
|
}
|
|
966
992
|
|
|
967
993
|
.toc-search {
|
|
@@ -975,11 +1001,14 @@ html.no-transition, html.no-transition * { transition: none !important; }
|
|
|
975
1001
|
}
|
|
976
1002
|
|
|
977
1003
|
.toc a {
|
|
1004
|
+
display: inline-flex; align-items: center; width: fit-content; max-width: 100%;
|
|
978
1005
|
font-size: 12px; color: #3f3f46; text-decoration: none;
|
|
979
|
-
padding:
|
|
980
|
-
|
|
1006
|
+
padding: 6px 10px; border-radius: 10px; background: #f5f5f5;
|
|
1007
|
+
border: 1px solid transparent; cursor: pointer; outline: none;
|
|
1008
|
+
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
|
|
981
1009
|
|
|
982
|
-
&:hover
|
|
1010
|
+
&:hover { background: #f4f4f5; border-color: #e4e4e7; color: #18181b; }
|
|
1011
|
+
&[data-active] { background: #f4f4f5; border-color: #d4d4d8; color: #18181b; box-shadow: inset 0 0 0 1px rgba(24,24,27,0.04); }
|
|
983
1012
|
&:focus-visible { background: #f4f4f5; color: #18181b; outline: 2px solid #18181b; outline-offset: 1px; }
|
|
984
1013
|
&[data-hidden] { display: none; }
|
|
985
1014
|
}
|
|
@@ -1344,17 +1373,29 @@ body:has(.source-overlay) { overflow: hidden; }
|
|
|
1344
1373
|
|
|
1345
1374
|
/* ── Collapsible ── */
|
|
1346
1375
|
|
|
1347
|
-
.gallery [data-trigger]:has(+ [data-content]) {
|
|
1376
|
+
.gallery .collapsible-demo [data-trigger]:has(+ [data-content]) {
|
|
1348
1377
|
padding: 8px 14px; border: 1px solid #d4d4d8; border-radius: 12px;
|
|
1349
1378
|
background: white; cursor: pointer; font-size: 14px; width: 100%; text-align: left;
|
|
1379
|
+
transition: border-color 0.15s ease, background-color 0.15s ease, border-radius 0.15s ease;
|
|
1350
1380
|
|
|
1351
|
-
&[aria-expanded="true"] { border-color: #
|
|
1381
|
+
&[aria-expanded="true"] { border-color: #a1a1aa; }
|
|
1352
1382
|
&:hover { background: #fafafa; }
|
|
1353
1383
|
}
|
|
1354
1384
|
|
|
1355
1385
|
.gallery .collapsible-demo [data-content] {
|
|
1356
1386
|
padding: 12px 14px; color: #52525b; font-size: 13px;
|
|
1357
|
-
|
|
1387
|
+
margin-top: -1px;
|
|
1388
|
+
border: 1px solid #e4e4e7; border-top: none; border-radius: 0 0 12px 12px;
|
|
1389
|
+
background: white;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
.gallery .collapsible-demo [data-open] [data-trigger]:has(+ [data-content]) {
|
|
1393
|
+
border-bottom-left-radius: 0;
|
|
1394
|
+
border-bottom-right-radius: 0;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.gallery .collapsible-demo [data-open] [data-content] {
|
|
1398
|
+
border-color: #a1a1aa;
|
|
1358
1399
|
}
|
|
1359
1400
|
|
|
1360
1401
|
/* ── Pagination ── */
|
|
@@ -1460,8 +1501,9 @@ body:has(.source-overlay) { overflow: hidden; }
|
|
|
1460
1501
|
.toc-search { border-color: #334155; background: #0f172a; color: #e4e4e7; }
|
|
1461
1502
|
.toc-search::placeholder { color: #52525b; }
|
|
1462
1503
|
.toc-label { color: #52525b; }
|
|
1463
|
-
.toc a { color: #71717a; background: #0f172a; }
|
|
1464
|
-
.toc a:hover
|
|
1504
|
+
.toc a { color: #71717a; background: #0f172a; border-color: transparent; }
|
|
1505
|
+
.toc a:hover { background: #172554; border-color: #1e3a8a; color: #a1a1aa; }
|
|
1506
|
+
.toc a[data-active] { background: #172554; border-color: #1e3a8a; color: #e4e4e7; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04); }
|
|
1465
1507
|
.toc-detail { border-left-color: #334155; background: #0f172a; }
|
|
1466
1508
|
.toc-detail h3 { color: #e4e4e7; }
|
|
1467
1509
|
.toc-detail .toc-desc { color: #71717a; }
|
|
@@ -1516,7 +1558,8 @@ body:has(.source-overlay) { overflow: hidden; }
|
|
|
1516
1558
|
|
|
1517
1559
|
/* Checkbox / Switch */
|
|
1518
1560
|
.gallery [role="checkbox"], .gallery [role="switch"] { border-color: #3f3f46; background: #1e293b; color: #e4e4e7; }
|
|
1519
|
-
.gallery [role="checkbox"][data-checked]
|
|
1561
|
+
.gallery [role="checkbox"][data-checked] { background: #172554; border-color: #18181b; color: #a1a1aa; }
|
|
1562
|
+
.gallery [role="switch"][data-checked] { background: #1d4ed8; border-color: #2563eb; color: #eff6ff; }
|
|
1520
1563
|
|
|
1521
1564
|
/* Menu */
|
|
1522
1565
|
.gallery [aria-haspopup="menu"] { border-color: #3f3f46; background: #1e293b; color: #e4e4e7; }
|
package/docs/ui/index.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
10
|
<link rel="stylesheet" href="index.css">
|
|
11
11
|
<style>body { opacity: 0; } body.ready { opacity: 1; transition: opacity 200ms ease-in; }</style>
|
|
12
|
-
<script defer src="../dist/rip.min.js" data-src="bundle" data-mount="WidgetGallery"
|
|
12
|
+
<script defer src="../dist/rip.min.js" data-src="bundle.json" data-mount="WidgetGallery">
|
|
13
13
|
</script>
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
@@ -103,8 +103,9 @@ export WidgetGallery = component
|
|
|
103
103
|
return unless entry
|
|
104
104
|
sourceName = entry.name
|
|
105
105
|
sourceLines = entry.lines
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
src = window.__RIP__?.components?.read("_pkg/ui/#{id}.rip")
|
|
107
|
+
return unless src
|
|
108
|
+
sourceCode = src
|
|
108
109
|
_closeSource: -> sourceCode = null
|
|
109
110
|
_stopProp: (e) -> e.stopPropagation()
|
|
110
111
|
_ensureHljs: (cb) ->
|
|
@@ -228,7 +229,7 @@ export WidgetGallery = component
|
|
|
228
229
|
tocData := [
|
|
229
230
|
{ id: 'select', name: 'Select', lines: 135, desc: 'Dropdown with typeahead, keyboard nav, ARIA listbox.', props: ['@value', '@placeholder', '@disabled'], events: ['change'], keys: ['↕', 'Enter', 'Space', 'Esc', 'Home', 'End', 'type-ahead'], attrs: ['$open', '$placeholder', '$disabled', '$value', '$highlighted', '$selected'] }
|
|
230
231
|
{ id: 'combobox', name: 'Combobox', lines: 118, desc: 'Filterable input + listbox for search-as-you-type.', props: ['@query', '@items', '@placeholder', '@disabled', '@autoHighlight'], events: ['filter', 'select'], keys: ['↕', 'Enter', 'Esc', 'Tab'], attrs: ['$open', '$disabled', '$clear', '$value', '$highlighted', '$empty'] }
|
|
231
|
-
{ id: 'multi-select', name: 'MultiSelect', lines:
|
|
232
|
+
{ id: 'multi-select', name: 'MultiSelect', lines: 204, desc: 'Multi-select with chips, filtering, and keyboard nav.', props: ['@value', '@items', '@placeholder', '@disabled'], events: ['change'], keys: ['↕', 'Enter', 'Esc', 'Backspace', 'Tab'], attrs: ['$open', '$disabled', '$chips', '$chip', '$remove', '$highlighted', '$selected'] }
|
|
232
233
|
{ id: 'autocomplete', name: 'Autocomplete', lines: 113, desc: 'Suggestion input — type to filter, select to fill.', props: ['@value', '@items', '@placeholder', '@disabled'], events: ['select'], keys: ['↕', 'Enter', 'Esc', 'Tab'], attrs: ['$open', '$disabled', '$clear'] }
|
|
233
234
|
{ id: 'checkbox', name: 'Checkbox', lines: 18, desc: 'Toggle with checkbox or switch semantics.', props: ['@checked', '@disabled', '@indeterminate', '@switch'], events: ['change'], keys: [], attrs: ['$checked', '$indeterminate', '$disabled'] }
|
|
234
235
|
{ id: 'toggle', name: 'Toggle', lines: 14, desc: 'Two-state toggle button with pressed state.', props: ['@pressed', '@disabled'], events: ['change'], keys: [], attrs: ['$pressed', '$disabled'] }
|
|
@@ -646,7 +647,7 @@ export WidgetGallery = component
|
|
|
646
647
|
# MULTI-SELECT
|
|
647
648
|
# ========================================================================
|
|
648
649
|
.section id: "multi-select"
|
|
649
|
-
SectionHead tag: "multi-select", name: "MultiSelect", lines:
|
|
650
|
+
SectionHead tag: "multi-select", name: "MultiSelect", lines: 204
|
|
650
651
|
.section-desc "Multi-select with chips, filtering, and keyboard navigation."
|
|
651
652
|
.demo-row
|
|
652
653
|
.demo-label "Pick colors"
|
|
@@ -677,7 +678,6 @@ export WidgetGallery = component
|
|
|
677
678
|
code "$chips"
|
|
678
679
|
code "$chip"
|
|
679
680
|
code "$remove"
|
|
680
|
-
code "$clear"
|
|
681
681
|
code "$highlighted"
|
|
682
682
|
code "$selected"
|
|
683
683
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.1",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
7
7
|
"workspaces": [
|
|
8
|
+
"examples/*",
|
|
8
9
|
"packages/*"
|
|
9
10
|
],
|
|
11
|
+
"catalog": {
|
|
12
|
+
"typescript": "5.9.3"
|
|
13
|
+
},
|
|
10
14
|
"browser": "docs/dist/rip.min.js",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
@@ -35,7 +39,8 @@
|
|
|
35
39
|
"bump": "bun scripts/bump.js",
|
|
36
40
|
"gen:dom": "bun scripts/gen-dom.js",
|
|
37
41
|
"gallery": "bun scripts/gallery.js",
|
|
38
|
-
"bundle:demo": "bun scripts/bundle-app.js docs/demo -o docs/example/index.json -t 'Rip App Demo'",
|
|
42
|
+
"bundle:demo": "bun scripts/bundle-app.js docs/demo/routes --prefix _route --css docs/demo/css -o docs/example/index.json -t 'Rip App Demo'",
|
|
43
|
+
"bundle:ui": "bun scripts/bundle-app.js packages/ui/browser/components --prefix _pkg/ui -o docs/ui/bundle.json -t 'Rip UI'",
|
|
39
44
|
"parser": "bun src/grammar/solar.rip -o src/parser.js src/grammar/grammar.rip",
|
|
40
45
|
"postinstall": "node scripts/postinstall.js --quiet",
|
|
41
46
|
"link-local": "bun scripts/link-local.js",
|
|
@@ -86,6 +91,7 @@
|
|
|
86
91
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
87
92
|
"license": "MIT",
|
|
88
93
|
"devDependencies": {
|
|
89
|
-
"
|
|
94
|
+
"@types/bun": "1.3.14",
|
|
95
|
+
"typescript": "catalog:"
|
|
90
96
|
}
|
|
91
97
|
}
|
package/rip-loader.js
CHANGED
|
@@ -2,13 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
import { plugin } from "bun";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
import { dirname, resolve as resolvePath } from "path";
|
|
6
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
7
|
import { compileToJS, formatError } from "./src/compiler.js";
|
|
8
|
+
// Register the full schema runtime provider so .rip files containing
|
|
9
|
+
// `schema :model` blocks compile correctly inside spawned workers.
|
|
10
|
+
// Workers are launched with `--preload rip-loader.js` and otherwise
|
|
11
|
+
// would call compileToJS without ever registering a provider.
|
|
12
|
+
import "./src/schema/loader-server.js";
|
|
13
|
+
|
|
14
|
+
// ── Undeclared-import diagnostic ────────────────────────────────────────
|
|
15
|
+
// Walk up from an importer to its nearest package.json, then verify that any
|
|
16
|
+
// `@rip-lang/<pkg>` specifier is declared in dependencies/devDependencies/
|
|
17
|
+
// peerDependencies/optionalDependencies (or is the package's own self-import).
|
|
18
|
+
//
|
|
19
|
+
// Throws a clear error before `import.meta.resolve` is even attempted, so
|
|
20
|
+
// "works on my machine" failures rooted in link-global rescue surface loudly
|
|
21
|
+
// instead of silently shipping.
|
|
22
|
+
const declarationCache = new Map(); // importerDir → { pkgName, declared } | null
|
|
23
|
+
|
|
24
|
+
function getDeclarationInfo(importerPath) {
|
|
25
|
+
const start = dirname(importerPath);
|
|
26
|
+
if (declarationCache.has(start)) return declarationCache.get(start);
|
|
27
|
+
let cur = start;
|
|
28
|
+
let info = null;
|
|
29
|
+
while (true) {
|
|
30
|
+
const pkgPath = resolvePath(cur, 'package.json');
|
|
31
|
+
if (existsSync(pkgPath)) {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
34
|
+
info = {
|
|
35
|
+
pkgName: pkg.name || null,
|
|
36
|
+
declared: new Set([
|
|
37
|
+
...Object.keys(pkg.dependencies || {}),
|
|
38
|
+
...Object.keys(pkg.devDependencies || {}),
|
|
39
|
+
...Object.keys(pkg.peerDependencies || {}),
|
|
40
|
+
...Object.keys(pkg.optionalDependencies || {}),
|
|
41
|
+
]),
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
info = { pkgName: null, declared: new Set() };
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(cur);
|
|
49
|
+
if (parent === cur) break;
|
|
50
|
+
cur = parent;
|
|
51
|
+
}
|
|
52
|
+
declarationCache.set(start, info);
|
|
53
|
+
return info;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function assertDeclaredRipImport(importerPath, specifier) {
|
|
57
|
+
const m = specifier.match(/^(@rip-lang\/[^\/]+)/);
|
|
58
|
+
if (!m) return;
|
|
59
|
+
const pkgKey = m[1];
|
|
60
|
+
const info = getDeclarationInfo(importerPath);
|
|
61
|
+
if (!info) return; // ad-hoc script outside any package — don't block
|
|
62
|
+
if (info.pkgName === pkgKey) return; // self-import
|
|
63
|
+
if (info.declared.has(pkgKey)) return;
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Import of '${pkgKey}' is not declared in package.json. ` +
|
|
66
|
+
`Run \`bun add ${pkgKey}\` (or use \`workspace:*\` inside this monorepo).`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
6
69
|
|
|
7
70
|
await plugin({
|
|
8
71
|
name: "rip-loader",
|
|
9
72
|
async setup(build) {
|
|
10
|
-
const { readFileSync } = await import("fs");
|
|
11
|
-
|
|
12
73
|
// Handle .rip files
|
|
13
74
|
build.onLoad({ filter: /\.rip$/ }, async (args) => {
|
|
14
75
|
try {
|
|
@@ -20,6 +81,7 @@ await plugin({
|
|
|
20
81
|
// is broken in plugin handlers — so we use import.meta.resolve, which
|
|
21
82
|
// resolves from this file's location (inside the global node_modules tree).
|
|
22
83
|
js = js.replace(/(from\s+|import\s*\()(['"])(@rip-lang\/[^'"]+)\2/g, (match, prefix, quote, specifier) => {
|
|
84
|
+
assertDeclaredRipImport(args.path, specifier);
|
|
23
85
|
try {
|
|
24
86
|
return `${prefix}${quote}${fileURLToPath(import.meta.resolve(specifier))}${quote}`;
|
|
25
87
|
} catch {
|
package/src/AGENTS.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# Compiler Subsystem — Agent Guide
|
|
2
2
|
|
|
3
|
-
This covers `compiler.js`, `lexer.js`, `components.js`, `browser.js`, `
|
|
3
|
+
This covers `compiler.js`, `lexer.js`, `components.js`, `browser.js`, `stdlib.js`, `types.js`, `dts.js`, `typecheck.js`, the `schema/` subdirectory, and the `grammar/` directory. The schema feature lives in `src/schema/` (entry `src/schema/schema.js`, imported via relative paths like `./schema/schema.js` from sibling modules).
|
|
4
|
+
|
|
5
|
+
The Rip App framework — stash, resource, timing, components store, router, renderer, launch, ARIA helpers — used to live alongside the compiler at `src/app.rip`. It now lives at `packages/app/index.rip` (peer of `packages/server/`, `packages/ui/`, etc.). The browser bundle still includes it via `scripts/build.js`; the source-tree split just reflects that it's user-land Rip code, not a compiler internal.
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## Module Map — browser-side vs CLI-only
|
|
8
10
|
|
|
9
|
-
The browser bundle (`docs/dist/rip.min.js`) is built from `src/browser.js` plus the compiled `
|
|
11
|
+
The browser bundle (`docs/dist/rip.min.js`) is built from `src/browser.js` plus the compiled `packages/app/index.rip`. Every module statically reachable from either entry ends up in the bundle. `scripts/check-bundle-graph.js` walks both entries on every `bun run build` and fails if any reachable file matches a forbidden list.
|
|
10
12
|
|
|
11
13
|
| Module | Browser? | Purpose |
|
|
12
14
|
| --- | --- | --- |
|
|
13
15
|
| `src/browser.js` | yes (entry) | `<script type="text/rip">` discovery, `processRipScripts`, `importRip`, REPL |
|
|
14
|
-
| `
|
|
16
|
+
| `packages/app/index.rip` | yes (entry) | Rip App framework runtime: stash, resource, timing, components store, router, renderer, launch, ARIA helpers |
|
|
15
17
|
| `src/parser.js` | yes | generated LR table |
|
|
16
18
|
| `src/lexer.js` | yes | tokenizer + rewriter pipeline |
|
|
17
19
|
| `src/compiler.js` | yes | codegen + reactive runtime + component runtime + `compileToJS` + `setTypesEmitter` hook + `emitEnum` |
|
|
18
20
|
| `src/components.js` | yes | render rewriter + component runtime |
|
|
21
|
+
| `src/stdlib.js` | yes | `getStdlibCode()` runtime preamble (p / pp / pr / pj / kind / abort / ...) + importable `stringify(value)` + `STDLIB_TYPE_DECLS` for typecheck.js |
|
|
19
22
|
| `src/schema/schema.js` | yes (via `./schema/schema.js`) | lexer rewrite + body parser + emitSchema codegen + `setSchemaRuntimeProvider` hook (no fragment imports) |
|
|
20
23
|
| `src/schema/loader-browser.js` | yes (browser only, via `./schema/loader-browser.js`) | imports validate + browser-stubs fragments; eager-installs browser runtime; registers provider |
|
|
21
24
|
| `src/schema/loader-server.js` | **no** (CLI / server / tests, via `./schema/loader-server.js`) | imports all five fragments; eager-installs migration runtime; registers provider |
|
|
@@ -30,8 +33,8 @@ The browser bundle (`docs/dist/rip.min.js`) is built from `src/browser.js` plus
|
|
|
30
33
|
| `src/sourcemaps.js` | yes | inline source-map generation |
|
|
31
34
|
| `src/generated/dom-tags.js` | yes | HTML/SVG tag set for render-block tag detection |
|
|
32
35
|
| `src/generated/dom-events.js` | yes | event-name set for `onClick`/`onKeydown` auto-wire |
|
|
33
|
-
| `src/
|
|
34
|
-
| `src/schema/dts
|
|
36
|
+
| `src/dts.js` | **no** | `.d.ts` emitter + intrinsic decl tables for the type system — CLI / typecheck only |
|
|
37
|
+
| `src/schema/dts.js` | **no** | `.d.ts` emitter for schema declarations — CLI / typecheck only |
|
|
35
38
|
| `src/typecheck.js` | **no** | TypeScript LSP integration — CLI only |
|
|
36
39
|
| `src/repl.js` | **no** | interactive CLI REPL |
|
|
37
40
|
|
|
@@ -43,22 +46,22 @@ The same pattern is used twice — once for `.d.ts` emission, once for the schem
|
|
|
43
46
|
|
|
44
47
|
`compiler.js` exports `setTypesEmitter(fn)`. The default emitter is `null`. The two `compile()` callsites that produce `.d.ts` output guard with `(typeTokens && _typesEmitter)` and silently skip if no emitter is registered.
|
|
45
48
|
|
|
46
|
-
`src/
|
|
49
|
+
`src/dts.js` calls `setTypesEmitter(emitTypes)` at module load. Any caller that wants `.d.ts` output side-effect-imports `dts.js`:
|
|
47
50
|
|
|
48
51
|
```javascript
|
|
49
52
|
// CLI entry — bin/rip
|
|
50
|
-
import '../src/
|
|
53
|
+
import '../src/dts.js'; // installs emitter
|
|
51
54
|
|
|
52
55
|
// LSP integration
|
|
53
|
-
import { ... } from './
|
|
56
|
+
import { ... } from './dts.js';
|
|
54
57
|
|
|
55
58
|
// Test runner that exercises type emission
|
|
56
|
-
import '../src/
|
|
59
|
+
import '../src/dts.js';
|
|
57
60
|
```
|
|
58
61
|
|
|
59
|
-
The browser bundle never imports `
|
|
62
|
+
The browser bundle never imports `dts.js`, so the emitter stays null and the `.d.ts` path is dead code that the bundler prunes.
|
|
60
63
|
|
|
61
|
-
**Failure mode to remember:** If you write code that calls `compile(source, { types: 'emit' })` and inspects `result.dts`, you **must** import `src/
|
|
64
|
+
**Failure mode to remember:** If you write code that calls `compile(source, { types: 'emit' })` and inspects `result.dts`, you **must** import `src/dts.js` (directly or indirectly) somewhere in that code path. Without it, `result.dts` is `null` regardless of source content. Symptom: types emission "silently does nothing" — no error, no warning, just empty output. The fix is one line: `import '../src/dts.js';`.
|
|
62
65
|
|
|
63
66
|
The schema runtime uses an analogous hook: `src/schema/schema.js` exports `setSchemaRuntimeProvider(fn)`, default null. `src/schema/loader-server.js` and `src/schema/loader-browser.js` are the two providers. CLI / tests / server side-effect-import `./schema/loader-server.js` (full migration runtime, all four modes). The browser bundle (`src/browser.js`) side-effect-imports `./schema/loader-browser.js` (validate + browser-stubs only). Same failure mode applies — call `getSchemaRuntime()` without registering a provider and you get a clear error pointing at which loader to import.
|
|
64
67
|
|
|
@@ -185,6 +188,8 @@ Complete node reference:
|
|
|
185
188
|
['++', expr, isPostfix] ['--', expr, isPostfix]
|
|
186
189
|
|
|
187
190
|
// Control Flow
|
|
191
|
+
// `elseBlock` may itself be another `['if', ...]` to form an `else if` chain
|
|
192
|
+
// (right-recursive nesting), or any other branch shape for a terminal else.
|
|
188
193
|
['if', condition, thenBlock, elseBlock?]
|
|
189
194
|
['unless', condition, body]
|
|
190
195
|
['?:', condition, thenExpr, elseExpr]
|
|
@@ -236,15 +241,15 @@ Complete node reference:
|
|
|
236
241
|
Tokens are `[tag, val]` arrays with extra properties:
|
|
237
242
|
|
|
238
243
|
- `.pre` — whitespace count before token
|
|
239
|
-
- `.data` — metadata like `{ await,
|
|
244
|
+
- `.data` — metadata like `{ await, optional, quote, invert, parsedValue }`
|
|
240
245
|
- `.loc` — `{ r, c, n }`
|
|
241
246
|
- `.spaced` — sugar for `.pre > 0`
|
|
242
247
|
- `.newLine` — whether preceded by newline
|
|
243
248
|
|
|
244
249
|
Identifier suffixes:
|
|
245
250
|
|
|
246
|
-
- `!` sets `.data.
|
|
247
|
-
- `?` sets `.data.
|
|
251
|
+
- `!` sets `.data.bang = true` — a neutral "trailing `!`" flag resolved by context downstream: dammit/`await` at a call site (`fetch!` → `await fetch()`), or the void marker at a function definition (`foo! = ->`, `def foo!` → no implicit return). Void-ness is stamped onto the function node as `isVoid` by `applyVoidMarker` and read locally by the arrow emitters; `def` reads `meta(name, 'bang')` directly.
|
|
252
|
+
- `?` sets `.data.optional = true` (existence check on values; optional marker on prop/type-field names)
|
|
248
253
|
- `as!` in loops emits `FORASAWAIT` for `for await`
|
|
249
254
|
|
|
250
255
|
Tagged template bridge:
|
|
@@ -410,7 +415,7 @@ Block factories need locals and `ctx.member` references instead of `this._elN` a
|
|
|
410
415
|
- `_factoryVars` — variables that need local `let` declarations
|
|
411
416
|
- `_fragChildren` — fragment-to-children tracking for removals
|
|
412
417
|
- `_pushEffect(body)` — emits `__effect(...)` or `disposers.push(__effect(...))`
|
|
413
|
-
- `_loopVarStack` — threads loop variables through nested factories
|
|
418
|
+
- `_loopVarStack` — threads loop variables through nested factories. Each frame is `{ itemVar, indexVar, reactiveSource }`; `reactiveSource` is computed once when the loop is emitted (via `hasReactiveDeps(collection)`) and tells `hasReactiveDeps` to treat direct member access rooted at `itemVar`/`indexVar` (`item.foo`, `item[0]`, `item.a.b`) as reactive. Alias and destructuring forms are not tracked.
|
|
414
419
|
|
|
415
420
|
Factory mode is entered in `emitConditionBranch` and `emitTemplateLoop` via save/restore of `[_createLines, _setupLines, _factoryMode, _factoryVars]`.
|
|
416
421
|
|
|
@@ -425,6 +430,13 @@ Methods named `onClick`, `onKeydown`, `onMouseenter`, etc. automatically bind to
|
|
|
425
430
|
- lifecycle exclusion: `onError` is not auto-wired
|
|
426
431
|
- after bumping `typescript`, refresh the generated DOM metadata with `bun run gen:dom`
|
|
427
432
|
|
|
433
|
+
### Inherited Props (`extends <tag>`)
|
|
434
|
+
|
|
435
|
+
User-facing docs: [docs/RIP-LANG.md](../docs/RIP-LANG.md) → "Inherited Props".
|
|
436
|
+
Runtime entry points to grep when working on this: `_bindInheritedTarget`,
|
|
437
|
+
`_setRestProp`, `_applyRestToInheritedEl`, `_inheritsTag`. Tests:
|
|
438
|
+
[test/rip/components.rip](../test/rip/components.rip) under "extends ...".
|
|
439
|
+
|
|
428
440
|
### Generated DOM Tag Sets
|
|
429
441
|
|
|
430
442
|
- `src/generated/dom-tags.js` is generated from TypeScript's `HTMLElementTagNameMap` and `SVGElementTagNameMap`
|
|
@@ -461,30 +473,35 @@ Compile-time optimizations:
|
|
|
461
473
|
- array-based `state.blocks[]`
|
|
462
474
|
- `state.keys = items.slice()` for default item-as-key behavior
|
|
463
475
|
|
|
464
|
-
### Nested Loop Variable Collision (
|
|
476
|
+
### Nested Loop Variable Collision (fixed)
|
|
465
477
|
|
|
466
|
-
|
|
467
|
-
|
|
478
|
+
Historically, `emitTemplateLoop` would auto-allocate `i` for any
|
|
479
|
+
outer loop with no explicit index. An inner `for v, i in ...` then
|
|
480
|
+
explicitly bound `i`, and both ended up as positional parameters of
|
|
481
|
+
the deepest factory's patch function:
|
|
468
482
|
|
|
469
483
|
```javascript
|
|
470
|
-
//
|
|
471
|
-
p(ctx, v, i, item, i) { ... } // duplicate `i` —
|
|
484
|
+
// Pre-fix output:
|
|
485
|
+
p(ctx, v, i, item, i) { ... } // duplicate `i` — strict-mode SyntaxError
|
|
472
486
|
```
|
|
473
487
|
|
|
474
|
-
The
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
488
|
+
The fix in `emitTemplateLoop` (`src/components.js`) calls
|
|
489
|
+
`_collectExplicitLoopIdentifiers(body, usedNames)` to pre-scan the
|
|
490
|
+
entire loop body for every explicit `for-in`/`for-of`/`for-as` var,
|
|
491
|
+
then picks the first conventional letter (`i,j,k,l,m,n`) that's not
|
|
492
|
+
in `usedNames`. If every letter collides, it falls back to
|
|
493
|
+
`__rip_idx${depth}` — a name no normal user identifier writes.
|
|
478
494
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
495
|
+
The same nested case now compiles cleanly:
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
function create_block_outer(ctx, item, j) { ... }
|
|
499
|
+
function create_block_inner(ctx, v, i, item, j) { ... } // unique
|
|
500
|
+
```
|
|
482
501
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
`emitFor*` path closes over the block into a patch function — search
|
|
487
|
-
for sites that build the `p(ctx, ...args)` signature in `compiler.js`.
|
|
502
|
+
User-explicit collisions (`for x, i in xs / for y, i in ys`) are
|
|
503
|
+
still a real strict-mode error and are intentionally left to the
|
|
504
|
+
user — the compiler doesn't silently rewrite the names you typed.
|
|
488
505
|
|
|
489
506
|
### Error Boundaries
|
|
490
507
|
|
|
@@ -556,6 +573,16 @@ The browser bundle is an IIFE loaded with `<script defer>`, not `type="module"`,
|
|
|
556
573
|
|
|
557
574
|
`let` declarations are stripped so values can persist in sloppy-mode eval, while `const` is hoisted to `globalThis`.
|
|
558
575
|
|
|
576
|
+
### `window.__RIP__` — same surface on both compile paths (don't break this)
|
|
577
|
+
|
|
578
|
+
`processRipScripts()` has two compile paths: with `data-router` it calls `app.launch()`; without `data-router` it inlines the bundles into one async-IIFE eval. Both paths expose the **same** debug surface on `window.__RIP__`, and consumers (e.g. the docs UI gallery's view-source feature) read from it indiscriminately:
|
|
579
|
+
|
|
580
|
+
- `window.__RIP__.components.read("components/<id>.rip")` — returns the bundled `.rip` source text.
|
|
581
|
+
|
|
582
|
+
`launch()` already wires this up (see `app.rip` ~1190). The no-router path used to silently skip it, so `window.__RIP__` was undefined for any deploy that used `data-src="bundle.json"` alone — view-source UIs would silently fail. The fix mirrors `launch()`'s setup: build a `createComponents()` store from the bundles and expose it on `window.__RIP__.components` (see `browser.js` ~226–239).
|
|
583
|
+
|
|
584
|
+
**Invariant:** any future refactor of either compile path must keep `window.__RIP__.components.read(path)` working after boot. The regression test that locks this in lives in `test/bundle.test.js` (boot-simulation driver — fakes a `<script src="rip.min.js" data-src="bundle.json">` runtime tag and a synthetic bundle, awaits `globalThis.__ripScriptsReady`, asserts `read()` returns the source). If you delete or move that block in `browser.js`, that test will fail with `window.__RIP__ missing — no-router path did not wire components store`.
|
|
585
|
+
|
|
559
586
|
---
|
|
560
587
|
|
|
561
588
|
## Reactivity Implementation
|
|
@@ -626,11 +653,11 @@ enum Status
|
|
|
626
653
|
Type emission is split across two files by execution context:
|
|
627
654
|
|
|
628
655
|
- `types.js` (browser-side, ~21 KB) — `installTypeSupport(Lexer)` adds `rewriteTypes()` to strip type annotations from the token stream so user-typed Rip parses. This is the only thing the browser needs from type machinery.
|
|
629
|
-
- `
|
|
656
|
+
- `dts.js` (CLI/LSP only, ~38 KB) — `emitTypes(tokens, sexpr, source)` generates `.d.ts`, plus `tsType`, `emitComponentTypes`, and the intrinsic declaration tables (`INTRINSIC_TYPE_DECLS`, `SIGNAL_*`, `COMPUTED_*`, `EFFECT_*`, etc.). Registers itself with the compiler at module load via `setTypesEmitter()`.
|
|
630
657
|
|
|
631
658
|
`emitEnum` (runtime JS for `enum` blocks) lives in `compiler.js` next to the rest of the codegen dispatch — it's not type machinery, it's real runtime emission.
|
|
632
659
|
|
|
633
|
-
`typecheck.js` (CLI only) drives `rip check`, mediates TypeScript diagnostics, and side-effect-imports `
|
|
660
|
+
`typecheck.js` (CLI only) drives `rip check`, mediates TypeScript diagnostics, and side-effect-imports `dts.js` for the intrinsic decl tables.
|
|
634
661
|
|
|
635
662
|
Types are processed at the token layer before parsing.
|
|
636
663
|
|
|
@@ -664,10 +691,10 @@ several files by execution context:
|
|
|
664
691
|
bundle. Server loader pulls all five; browser loader pulls only
|
|
665
692
|
validate + browser-stubs. Bun's tree-shaker uses these import sets to
|
|
666
693
|
omit server-only fragments from `docs/dist/rip.min.js`.
|
|
667
|
-
- `src/schema/dts
|
|
694
|
+
- `src/schema/dts.js` (CLI/LSP only) — `emitSchemaTypes` walks
|
|
668
695
|
parsed schema s-expressions and emits `declare const Foo: Schema<...>`
|
|
669
696
|
lines for the TypeScript language service. Imported only by
|
|
670
|
-
`
|
|
697
|
+
`dts.js` and `typecheck.js`. The `dts` name signals
|
|
671
698
|
that this is a compile-time `.d.ts` emitter, not a `runtime-*` fragment.
|
|
672
699
|
|
|
673
700
|
### Lexer path
|