rip-lang 3.9.0 → 3.9.2
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 +29 -0
- package/README.md +14 -14
- package/docs/RIP-LANG.md +10 -1
- package/docs/dist/rip-ui.min.js +123 -123
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +173 -173
- 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 +126 -0
- package/package.json +1 -1
- package/src/browser.js +18 -9
- package/src/compiler.js +3 -0
- package/src/components.js +76 -17
- package/src/grammar/grammar.rip +2 -2
- package/src/grammar/parser.js +360 -0
- package/src/lexer.js +10 -0
- package/src/parser.js +5 -6
- package/docs/demo.html +0 -342
- 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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Sierpinski Triangle — Rip UI</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
8
|
+
<style>
|
|
9
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
|
+
body { background: #0a0a0a; color: white; font-family: 'JetBrains Mono', monospace; overflow: hidden; }
|
|
11
|
+
.scene { position: relative; width: 100vw; height: 100vh; }
|
|
12
|
+
.stats { position: fixed; top: 20px; left: 24px; z-index: 10; }
|
|
13
|
+
.stats-title { font-size: 18px; font-weight: bold; }
|
|
14
|
+
.stats-line { font-size: 13px; color: rgba(255,255,255,0.45); margin-top: 3px; }
|
|
15
|
+
.stats-note { font-size: 11px; color: rgba(255,255,255,0.25); margin-top: 8px; max-width: 280px; line-height: 1.5; }
|
|
16
|
+
.source-toggle { display: inline-block; margin-top: 10px; padding: 4px 10px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); border-radius: 4px; color: rgba(255,255,255,0.5); font-family: inherit; font-size: 12px; cursor: pointer; transition: all 0.2s; }
|
|
17
|
+
.source-toggle:hover { color: white; border-color: rgba(255,255,255,0.4); background: rgba(255,255,255,0.12); }
|
|
18
|
+
.source-panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 90vw); z-index: 100; background: rgba(10,10,10,0.92); backdrop-filter: blur(12px); border-left: 1px solid rgba(255,255,255,0.1); transform: translateX(100%); transition: transform 0.3s ease; overflow-y: auto; }
|
|
19
|
+
.source-panel.open { transform: translateX(0); }
|
|
20
|
+
.source-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.08); }
|
|
21
|
+
.source-label { font-size: 13px; font-weight: bold; color: rgba(255,255,255,0.6); letter-spacing: 0.05em; }
|
|
22
|
+
.source-close { background: none; border: none; color: rgba(255,255,255,0.4); font-size: 20px; cursor: pointer; padding: 4px 8px; border-radius: 4px; font-family: inherit; transition: all 0.2s; }
|
|
23
|
+
.source-close:hover { color: white; background: rgba(255,255,255,0.1); }
|
|
24
|
+
.source-code { padding: 16px 20px; font-size: 13px; line-height: 1.7; color: rgba(255,255,255,0.85); white-space: pre; overflow-x: auto; }
|
|
25
|
+
.container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(var(--scale, 1)); width: 1000px; height: 870px; transform-origin: center center; }
|
|
26
|
+
.dot {
|
|
27
|
+
position: absolute; width: 24px; height: 24px; border-radius: 50%;
|
|
28
|
+
display: flex; align-items: center; justify-content: center;
|
|
29
|
+
font-size: 10px; font-weight: bold; color: rgba(255,255,255,0.9);
|
|
30
|
+
user-select: none; cursor: default; transition: transform 0.1s;
|
|
31
|
+
background: hsl(calc(var(--h) + var(--off, 0)) 70% 50%);
|
|
32
|
+
}
|
|
33
|
+
.dot:hover { transform: scale(2.5); z-index: 1; }
|
|
34
|
+
</style>
|
|
35
|
+
<script type="module" src="https://shreeve.github.io/rip-lang/dist/rip-ui.min.js"></script>
|
|
36
|
+
<script>
|
|
37
|
+
function updateScale() {
|
|
38
|
+
var s = Math.min(window.innerWidth / 1350, window.innerHeight / 1250, 1);
|
|
39
|
+
document.documentElement.style.setProperty('--scale', s);
|
|
40
|
+
}
|
|
41
|
+
updateScale();
|
|
42
|
+
window.addEventListener('resize', updateScale);
|
|
43
|
+
</script>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<div class="source-panel" id="source-panel">
|
|
47
|
+
<div class="source-header">
|
|
48
|
+
<span class="source-label">SOURCE — 58 LINES OF RIP</span>
|
|
49
|
+
<button class="source-close" onclick="document.getElementById('source-panel').classList.remove('open')">×</button>
|
|
50
|
+
</div>
|
|
51
|
+
<pre class="source-code" id="source-code"></pre>
|
|
52
|
+
</div>
|
|
53
|
+
<script type="text/rip">
|
|
54
|
+
# Sierpinski Triangle — 729 dots with CSS custom property color cycling
|
|
55
|
+
# Static layout + one CSS variable update per frame for smooth animation.
|
|
56
|
+
|
|
57
|
+
App = component
|
|
58
|
+
counter := 1
|
|
59
|
+
fps := 0
|
|
60
|
+
|
|
61
|
+
dots = do ->
|
|
62
|
+
result = []
|
|
63
|
+
build = (x, y, s) ->
|
|
64
|
+
if s <= 25
|
|
65
|
+
result.push { x, y, id: result.length }
|
|
66
|
+
else
|
|
67
|
+
build x , y - s / 4, s / 2
|
|
68
|
+
build x - s / 2, y + s / 4, s / 2
|
|
69
|
+
build x + s / 2, y + s / 4, s / 2
|
|
70
|
+
build 500, 435, 1000
|
|
71
|
+
result
|
|
72
|
+
|
|
73
|
+
mounted: ->
|
|
74
|
+
start = performance.now()
|
|
75
|
+
frames = 0
|
|
76
|
+
last = start
|
|
77
|
+
raf = null
|
|
78
|
+
container = document.querySelector('.container')
|
|
79
|
+
tick = ->
|
|
80
|
+
now = performance.now()
|
|
81
|
+
secs = (now - start) / 1000
|
|
82
|
+
container.style.setProperty '--off', String(secs * 40 % 360)
|
|
83
|
+
c = Math.floor(secs % 10) + 1
|
|
84
|
+
counter = c if counter isnt c
|
|
85
|
+
frames++
|
|
86
|
+
if now - last >= 1000
|
|
87
|
+
fps = frames
|
|
88
|
+
frames = 0
|
|
89
|
+
last = now
|
|
90
|
+
raf = requestAnimationFrame tick
|
|
91
|
+
raf = requestAnimationFrame tick
|
|
92
|
+
document.addEventListener 'visibilitychange', ->
|
|
93
|
+
if document.hidden
|
|
94
|
+
cancelAnimationFrame raf
|
|
95
|
+
raf = null
|
|
96
|
+
else
|
|
97
|
+
last = performance.now()
|
|
98
|
+
frames = 0
|
|
99
|
+
raf = requestAnimationFrame tick
|
|
100
|
+
|
|
101
|
+
render
|
|
102
|
+
.scene
|
|
103
|
+
.stats
|
|
104
|
+
.stats-title "Sierpinski Triangle"
|
|
105
|
+
.stats-line dots.length + " dots · CSS color cycling · " + fps + " fps"
|
|
106
|
+
.stats-note "True HSL color cycling via CSS custom properties. Counter updates reactively once per second."
|
|
107
|
+
button.source-toggle @click: (-> document.getElementById('source-panel').classList.toggle('open')), "View Source"
|
|
108
|
+
|
|
109
|
+
.container
|
|
110
|
+
for dot in dots
|
|
111
|
+
.dot style: "left:" + (dot.x - 12) + "px;top:" + (dot.y - 12) + "px;--h:" + (dot.id / 729 * 360)
|
|
112
|
+
counter
|
|
113
|
+
|
|
114
|
+
App.new().mount document.body
|
|
115
|
+
|
|
116
|
+
# Populate source panel with this page's own Rip source
|
|
117
|
+
ripScript = document.querySelector('script[type="text/rip"]')
|
|
118
|
+
lines = ripScript.textContent.split("\n")
|
|
119
|
+
lines.shift() while lines.length and lines[0].trim() is ''
|
|
120
|
+
cutoff = lines.findIndex((l) -> l.includes('Populate source panel'))
|
|
121
|
+
lines = lines.slice(0, cutoff).filter((l, i, a) -> i < a.length - 1 or l.trim() isnt '')
|
|
122
|
+
sourceEl = document.getElementById('source-code')
|
|
123
|
+
sourceEl.textContent = lines.join("\n")
|
|
124
|
+
</script>
|
|
125
|
+
</body>
|
|
126
|
+
</html>
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -71,8 +71,14 @@ export { processRipScripts };
|
|
|
71
71
|
* Import a .rip file as an ES module
|
|
72
72
|
* Fetches the URL, compiles Rip→JS, dynamically imports via Blob URL
|
|
73
73
|
* Usage: const { launch } = await importRip('/ui.rip')
|
|
74
|
+
*
|
|
75
|
+
* Pre-compiled modules can be registered on importRip.modules to skip fetching.
|
|
76
|
+
* The rip-ui bundle uses this to embed ui.rip without a server round-trip.
|
|
74
77
|
*/
|
|
75
78
|
export async function importRip(url) {
|
|
79
|
+
for (const [key, mod] of Object.entries(importRip.modules)) {
|
|
80
|
+
if (url.includes(key)) return mod;
|
|
81
|
+
}
|
|
76
82
|
const source = await fetch(url).then(r => {
|
|
77
83
|
if (!r.ok) throw new Error(`importRip: ${url} (${r.status})`);
|
|
78
84
|
return r.text();
|
|
@@ -86,6 +92,7 @@ export async function importRip(url) {
|
|
|
86
92
|
URL.revokeObjectURL(blobUrl);
|
|
87
93
|
}
|
|
88
94
|
}
|
|
95
|
+
importRip.modules = {};
|
|
89
96
|
|
|
90
97
|
/**
|
|
91
98
|
* Browser Console REPL
|
|
@@ -128,14 +135,16 @@ if (typeof globalThis !== 'undefined') {
|
|
|
128
135
|
globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
129
136
|
}
|
|
130
137
|
|
|
131
|
-
// Auto-process
|
|
132
|
-
//
|
|
138
|
+
// Auto-process <script type="text/rip"> blocks.
|
|
139
|
+
// Deferred via queueMicrotask so bundled entry code (e.g. rip-ui.min.js registering
|
|
140
|
+
// importRip.modules) runs before script processing begins.
|
|
133
141
|
if (typeof document !== 'undefined') {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
globalThis.__ripScriptsReady = new Promise(resolve => {
|
|
143
|
+
const run = () => processRipScripts().then(resolve);
|
|
144
|
+
if (document.readyState === 'loading') {
|
|
145
|
+
document.addEventListener('DOMContentLoaded', () => queueMicrotask(run));
|
|
146
|
+
} else {
|
|
147
|
+
queueMicrotask(run);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
141
150
|
}
|
package/src/compiler.js
CHANGED
|
@@ -1094,6 +1094,9 @@ export class CodeGenerator {
|
|
|
1094
1094
|
if (!this.reactiveVars) this.reactiveVars = new Set();
|
|
1095
1095
|
let varName = str(name) ?? name;
|
|
1096
1096
|
this.reactiveVars.add(varName);
|
|
1097
|
+
if (this.is(expr, 'block') && expr.length > 2) {
|
|
1098
|
+
return `const ${varName} = __computed(() => ${this.generateFunctionBody(expr)})`;
|
|
1099
|
+
}
|
|
1097
1100
|
return `const ${varName} = __computed(() => ${this.generate(expr, 'value')})`;
|
|
1098
1101
|
}
|
|
1099
1102
|
|
package/src/components.js
CHANGED
|
@@ -17,6 +17,12 @@ const BIND_PREFIX = '__bind_';
|
|
|
17
17
|
const BIND_SUFFIX = '__';
|
|
18
18
|
|
|
19
19
|
const LIFECYCLE_HOOKS = new Set(['beforeMount', 'mounted', 'updated', 'beforeUnmount', 'unmounted']);
|
|
20
|
+
const BOOLEAN_ATTRS = new Set([
|
|
21
|
+
'disabled', 'hidden', 'readonly', 'required', 'checked', 'selected',
|
|
22
|
+
'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'multiple',
|
|
23
|
+
'novalidate', 'open', 'reversed', 'defer', 'async', 'formnovalidate',
|
|
24
|
+
'allowfullscreen', 'inert',
|
|
25
|
+
]);
|
|
20
26
|
|
|
21
27
|
// ============================================================================
|
|
22
28
|
// Standalone Utilities
|
|
@@ -51,6 +57,23 @@ function getMemberName(target) {
|
|
|
51
57
|
return null;
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Detect fragment root and collect direct child variables for proper removal.
|
|
62
|
+
* After insertBefore, a DocumentFragment is empty — .remove() is a no-op.
|
|
63
|
+
* Callers must remove each child element individually.
|
|
64
|
+
*/
|
|
65
|
+
function getFragChildren(rootVar, createLines, localizeVar) {
|
|
66
|
+
const root = localizeVar(rootVar);
|
|
67
|
+
if (!/_frag\d+$/.test(root)) return null;
|
|
68
|
+
const children = [];
|
|
69
|
+
const re = new RegExp(`^${root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.appendChild\\(([^)]+)\\);`);
|
|
70
|
+
for (const line of createLines) {
|
|
71
|
+
const m = localizeVar(line).match(re);
|
|
72
|
+
if (m) children.push(m[1]);
|
|
73
|
+
}
|
|
74
|
+
return children.length > 0 ? children : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
54
77
|
// ============================================================================
|
|
55
78
|
// Prototype Installation
|
|
56
79
|
// ============================================================================
|
|
@@ -211,8 +234,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
211
234
|
}
|
|
212
235
|
|
|
213
236
|
// ─────────────────────────────────────────────────────────────────────
|
|
214
|
-
// Implicit div for class-only selectors
|
|
215
|
-
// .card → div.card
|
|
237
|
+
// Implicit div for class-only or bare dot selectors
|
|
238
|
+
// .card → div.card | . (with children) → div
|
|
216
239
|
// ─────────────────────────────────────────────────────────────────────
|
|
217
240
|
if (tag === '.') {
|
|
218
241
|
let prevToken = i > 0 ? tokens[i - 1] : null;
|
|
@@ -223,6 +246,12 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
223
246
|
tokens.splice(i, 0, divToken);
|
|
224
247
|
return 2;
|
|
225
248
|
}
|
|
249
|
+
// Skip .('classes') — handled by dynamic classes handler below
|
|
250
|
+
if (!nextToken || nextToken[0] !== '(') {
|
|
251
|
+
token[0] = 'IDENTIFIER';
|
|
252
|
+
token[1] = 'div';
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
226
255
|
}
|
|
227
256
|
}
|
|
228
257
|
|
|
@@ -343,7 +372,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
343
372
|
isTemplateElement = true;
|
|
344
373
|
} else if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
|
|
345
374
|
isTemplateElement = true;
|
|
346
|
-
} else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'CALL_END' || tag === ')') {
|
|
375
|
+
} else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'STRING_END' || tag === 'CALL_END' || tag === ')') {
|
|
347
376
|
isTemplateElement = startsWithTag(tokens, i);
|
|
348
377
|
}
|
|
349
378
|
else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
|
|
@@ -614,8 +643,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
614
643
|
|
|
615
644
|
// Computed (derived)
|
|
616
645
|
for (const { name, expr } of derivedVars) {
|
|
617
|
-
|
|
618
|
-
|
|
646
|
+
if (this.is(expr, 'block') && expr.length > 2) {
|
|
647
|
+
const transformed = this.transformComponentMembers(expr);
|
|
648
|
+
const body = this.generateFunctionBody(transformed);
|
|
649
|
+
lines.push(` this.${name} = __computed(() => ${body});`);
|
|
650
|
+
} else {
|
|
651
|
+
const val = this.generateInComponent(expr, 'value');
|
|
652
|
+
lines.push(` this.${name} = __computed(() => ${val});`);
|
|
653
|
+
}
|
|
619
654
|
}
|
|
620
655
|
|
|
621
656
|
// Effects
|
|
@@ -991,10 +1026,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
991
1026
|
const eventName = key[2];
|
|
992
1027
|
// Bind method references to this
|
|
993
1028
|
if (typeof value === 'string' && this.componentMembers?.has(value)) {
|
|
994
|
-
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => this.${value}(e));`);
|
|
1029
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => this.${value}(e)));`);
|
|
995
1030
|
} else {
|
|
996
1031
|
const handlerCode = this.generateInComponent(value, 'value');
|
|
997
|
-
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
|
|
1032
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => (${handlerCode})(e)));`);
|
|
998
1033
|
}
|
|
999
1034
|
continue;
|
|
1000
1035
|
}
|
|
@@ -1048,7 +1083,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1048
1083
|
continue;
|
|
1049
1084
|
}
|
|
1050
1085
|
|
|
1051
|
-
if (
|
|
1086
|
+
if (BOOLEAN_ATTRS.has(key)) {
|
|
1087
|
+
if (this.hasReactiveDeps(value)) {
|
|
1088
|
+
this._setupLines.push(`__effect(() => { ${elVar}.toggleAttribute('${key}', !!${valueCode}); });`);
|
|
1089
|
+
} else {
|
|
1090
|
+
this._createLines.push(`if (${valueCode}) ${elVar}.setAttribute('${key}', '');`);
|
|
1091
|
+
}
|
|
1092
|
+
} else if (this.hasReactiveDeps(value)) {
|
|
1052
1093
|
this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('${key}', ${valueCode}); });`);
|
|
1053
1094
|
} else {
|
|
1054
1095
|
this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
|
|
@@ -1221,7 +1262,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1221
1262
|
if (hasEffects) {
|
|
1222
1263
|
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1223
1264
|
}
|
|
1224
|
-
|
|
1265
|
+
const condFragChildren = getFragChildren(rootVar, createLines, localizeVar);
|
|
1266
|
+
if (condFragChildren) {
|
|
1267
|
+
for (const child of condFragChildren) {
|
|
1268
|
+
factoryLines.push(` if (detaching) ${child}.remove();`);
|
|
1269
|
+
}
|
|
1270
|
+
} else {
|
|
1271
|
+
factoryLines.push(` if (detaching) ${localizeVar(rootVar)}.remove();`);
|
|
1272
|
+
}
|
|
1225
1273
|
factoryLines.push(` }`);
|
|
1226
1274
|
|
|
1227
1275
|
factoryLines.push(` };`);
|
|
@@ -1311,9 +1359,16 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1311
1359
|
}
|
|
1312
1360
|
factoryLines.push(` },`);
|
|
1313
1361
|
|
|
1314
|
-
// m() - mount
|
|
1362
|
+
// m() - mount (also repositions already-mounted blocks)
|
|
1363
|
+
const loopFragChildren = getFragChildren(itemNode, itemCreateLines, localizeVar);
|
|
1315
1364
|
factoryLines.push(` m(target, anchor) {`);
|
|
1316
|
-
|
|
1365
|
+
if (loopFragChildren) {
|
|
1366
|
+
for (const child of loopFragChildren) {
|
|
1367
|
+
factoryLines.push(` target.insertBefore(${child}, anchor);`);
|
|
1368
|
+
}
|
|
1369
|
+
} else {
|
|
1370
|
+
factoryLines.push(` target.insertBefore(${localizeVar(itemNode)}, anchor);`);
|
|
1371
|
+
}
|
|
1317
1372
|
factoryLines.push(` },`);
|
|
1318
1373
|
|
|
1319
1374
|
// p() - update
|
|
@@ -1340,7 +1395,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1340
1395
|
if (hasEffects) {
|
|
1341
1396
|
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1342
1397
|
}
|
|
1343
|
-
|
|
1398
|
+
if (loopFragChildren) {
|
|
1399
|
+
for (const child of loopFragChildren) {
|
|
1400
|
+
factoryLines.push(` if (detaching) ${child}.remove();`);
|
|
1401
|
+
}
|
|
1402
|
+
} else {
|
|
1403
|
+
factoryLines.push(` if (detaching) ${localizeVar(itemNode)}.remove();`);
|
|
1404
|
+
}
|
|
1344
1405
|
factoryLines.push(` }`);
|
|
1345
1406
|
|
|
1346
1407
|
factoryLines.push(` };`);
|
|
@@ -1363,14 +1424,12 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1363
1424
|
setupLines.push(` const ${itemVar} = items[${indexVar}];`);
|
|
1364
1425
|
setupLines.push(` const key = ${keyExpr};`);
|
|
1365
1426
|
setupLines.push(` let block = map.get(key);`);
|
|
1366
|
-
setupLines.push(` if (block) {`);
|
|
1367
|
-
setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
|
|
1368
|
-
setupLines.push(` } else {`);
|
|
1427
|
+
setupLines.push(` if (!block) {`);
|
|
1369
1428
|
setupLines.push(` block = ${blockName}(this, ${itemVar}, ${indexVar});`);
|
|
1370
1429
|
setupLines.push(` block.c();`);
|
|
1371
|
-
setupLines.push(` block.m(parent, anchor);`);
|
|
1372
|
-
setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
|
|
1373
1430
|
setupLines.push(` }`);
|
|
1431
|
+
setupLines.push(` block.m(parent, anchor);`);
|
|
1432
|
+
setupLines.push(` block.p(this, ${itemVar}, ${indexVar});`);
|
|
1374
1433
|
setupLines.push(` newMap.set(key, block);`);
|
|
1375
1434
|
setupLines.push(` }`);
|
|
1376
1435
|
setupLines.push(``);
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -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
|
|
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]'
|
|
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
|
]
|