rip-lang 3.10.14 → 3.12.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/browser.js CHANGED
@@ -13,7 +13,7 @@ export const BUILD_DATE = "0000-00-00@00:00:00GMT";
13
13
  import { compile, compileToJS, formatSExpr, getReactiveRuntime, getComponentRuntime } from './compiler.js';
14
14
 
15
15
  // Eagerly register Rip's reactive primitives on globalThis so that
16
- // framework code (ui.rip) can use them directly without the compiler
16
+ // framework code (app.rip) can use them directly without the compiler
17
17
  // needing to detect reactive operators in the source
18
18
  if (typeof globalThis !== 'undefined' && !globalThis.__rip) {
19
19
  new Function(getReactiveRuntime())();
@@ -71,10 +71,10 @@ export { processRipScripts };
71
71
  /**
72
72
  * Import a .rip file as an ES module
73
73
  * Fetches the URL, compiles Rip→JS, dynamically imports via Blob URL
74
- * Usage: const { launch } = await importRip('/ui.rip')
74
+ * Usage: const { launch } = await importRip('/app.rip')
75
75
  *
76
76
  * Pre-compiled modules can be registered on importRip.modules to skip fetching.
77
- * The rip-ui bundle uses this to embed ui.rip without a server round-trip.
77
+ * The browser bundle uses this to embed app.rip without a server round-trip.
78
78
  */
79
79
  export async function importRip(url) {
80
80
  for (const [key, mod] of Object.entries(importRip.modules)) {
@@ -85,13 +85,10 @@ export async function importRip(url) {
85
85
  return r.text();
86
86
  });
87
87
  const js = compileToJS(source);
88
- const blob = new Blob([js], { type: 'application/javascript' });
88
+ const header = `// ${url}\n`;
89
+ const blob = new Blob([header + js], { type: 'application/javascript' });
89
90
  const blobUrl = URL.createObjectURL(blob);
90
- try {
91
- return await import(blobUrl);
92
- } finally {
93
- URL.revokeObjectURL(blobUrl);
94
- }
91
+ return await import(blobUrl);
95
92
  }
96
93
  importRip.modules = {};
97
94
 
@@ -136,12 +133,12 @@ if (typeof globalThis !== 'undefined') {
136
133
  globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
137
134
  }
138
135
 
139
- // Auto-launch: if rip-ui is bundled and the page has component scripts or a data-url,
136
+ // Auto-launch: if app.rip is bundled and the page has component scripts or a data-url,
140
137
  // call launch() automatically with config from the script tag's data attributes.
141
138
  // Hash routing defaults to true (opt out with data-hash="false").
142
139
  async function autoLaunch() {
143
140
  if (globalThis.__ripLaunched) return;
144
- const ui = importRip.modules?.['ui.rip'];
141
+ const ui = importRip.modules?.['app.rip'];
145
142
  if (!ui?.launch) return;
146
143
  const cfg = document.querySelector('script[data-hash], script[data-url]');
147
144
  const url = cfg?.getAttribute('data-url') || '';
@@ -153,7 +150,7 @@ async function autoLaunch() {
153
150
  }
154
151
 
155
152
  // Auto-process <script type="text/rip"> blocks, then auto-launch if applicable.
156
- // Deferred via queueMicrotask so bundled entry code (e.g. rip-ui.min.js registering
153
+ // Deferred via queueMicrotask so bundled entry code (e.g. rip.min.js registering
157
154
  // importRip.modules) runs before script processing begins.
158
155
  if (typeof document !== 'undefined') {
159
156
  globalThis.__ripScriptsReady = new Promise(resolve => {
package/src/compiler.js CHANGED
@@ -871,9 +871,7 @@ export class CodeGenerator {
871
871
  } else if (typeof target === 'string' && this.reactiveVars?.has(target)) {
872
872
  targetCode = `${target}.value`;
873
873
  } else {
874
- this.suppressReactiveUnwrap = true;
875
874
  targetCode = this.generate(target, 'value');
876
- this.suppressReactiveUnwrap = false;
877
875
  }
878
876
 
879
877
  let valueCode = this.generate(value, 'value');
@@ -1012,11 +1010,15 @@ export class CodeGenerator {
1012
1010
  if (this.is(body, 'block') && body.length === 2) {
1013
1011
  let expr = body[1];
1014
1012
  if (!Array.isArray(expr) || expr[0] !== 'return') {
1015
- return `${prefix}${paramSyntax} => ${this.generate(expr, 'value')}`;
1013
+ let code = this.generate(expr, 'value');
1014
+ if (code[0] === '{') code = `(${code})`;
1015
+ return `${prefix}${paramSyntax} => ${code}`;
1016
1016
  }
1017
1017
  }
1018
1018
  if (!Array.isArray(body) || body[0] !== 'block') {
1019
- return `${prefix}${paramSyntax} => ${this.generate(body, 'value')}`;
1019
+ let code = this.generate(body, 'value');
1020
+ if (code[0] === '{') code = `(${code})`;
1021
+ return `${prefix}${paramSyntax} => ${code}`;
1020
1022
  }
1021
1023
  }
1022
1024
 
@@ -1279,6 +1281,12 @@ export class CodeGenerator {
1279
1281
 
1280
1282
  generateForOf(head, rest, context, sexpr) {
1281
1283
  let [vars, obj, own, guard, body] = rest;
1284
+
1285
+ if (context === 'value' && this.comprehensionDepth === 0) {
1286
+ let iterator = ['for-of', vars, obj, own];
1287
+ return this.generate(['comprehension', body, [iterator], guard ? [guard] : []], context);
1288
+ }
1289
+
1282
1290
  let [keyVar, valueVar] = Array.isArray(vars) ? vars : [vars];
1283
1291
  let objCode = this.generate(obj, 'value');
1284
1292
  let code = `for (const ${keyVar} in ${objCode}) `;
package/src/components.js CHANGED
@@ -7,7 +7,46 @@
7
7
  //
8
8
  // Naming: All render-tree generators use generate* (consistent with compiler).
9
9
 
10
- import { TEMPLATE_TAGS, SVG_TAGS } from './tags.js';
10
+ // ============================================================================
11
+ // HTML/SVG Tag Definitions
12
+ // ============================================================================
13
+
14
+ const HTML_TAGS = new Set([
15
+ 'html', 'head', 'title', 'base', 'link', 'meta', 'style',
16
+ 'body', 'address', 'article', 'aside', 'footer', 'header',
17
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section',
18
+ 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure',
19
+ 'hr', 'li', 'ol', 'p', 'pre', 'ul',
20
+ 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data',
21
+ 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', 's',
22
+ 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr',
23
+ 'area', 'audio', 'img', 'map', 'track', 'video',
24
+ 'embed', 'iframe', 'object', 'param', 'picture', 'portal', 'source',
25
+ 'svg', 'math', 'canvas',
26
+ 'noscript', 'script',
27
+ 'del', 'ins',
28
+ 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr',
29
+ 'button', 'datalist', 'fieldset', 'form', 'input', 'label', 'legend',
30
+ 'meter', 'optgroup', 'option', 'output', 'progress', 'select', 'textarea',
31
+ 'details', 'dialog', 'menu', 'summary',
32
+ 'slot', 'template'
33
+ ]);
34
+
35
+ const SVG_TAGS = new Set([
36
+ 'svg', 'g', 'defs', 'symbol', 'use', 'marker', 'clipPath', 'mask', 'pattern',
37
+ 'circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect',
38
+ 'text', 'textPath', 'tspan',
39
+ 'linearGradient', 'radialGradient', 'stop',
40
+ 'filter', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite',
41
+ 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight',
42
+ 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR',
43
+ 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology',
44
+ 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence',
45
+ 'animate', 'animateMotion', 'animateTransform', 'set', 'mpath',
46
+ 'desc', 'foreignObject', 'image', 'metadata', 'switch', 'title', 'view'
47
+ ]);
48
+
49
+ const TEMPLATE_TAGS = new Set([...HTML_TAGS, ...SVG_TAGS]);
11
50
 
12
51
  // ============================================================================
13
52
  // Constants
@@ -135,6 +174,18 @@ export function installComponentSupport(CodeGenerator, Lexer) {
135
174
  return isHtmlTag(name) || isComponent(name);
136
175
  };
137
176
 
177
+ let skipBalancedPair = (tokens, from, closer, opener) => {
178
+ let depth = 1;
179
+ let k = from;
180
+ while (k >= 0 && depth > 0) {
181
+ let kt = tokens[k][0];
182
+ if (kt === closer) depth++;
183
+ else if (kt === opener) depth--;
184
+ if (depth > 0) k--;
185
+ }
186
+ return k;
187
+ };
188
+
138
189
  let startsWithTag = (tokens, i) => {
139
190
  let j = i;
140
191
  while (j > 0) {
@@ -145,31 +196,21 @@ export function installComponentSupport(CodeGenerator, Lexer) {
145
196
  if (pt === 'INDENT' || pt === 'OUTDENT') {
146
197
  let jt = tokens[j][0];
147
198
  if (jt === 'CALL_END' || jt === ')') {
148
- let open = jt === 'CALL_END' ? 'CALL_START' : '(';
149
- let depth = 1;
150
- let k = j - 1;
151
- while (k >= 0 && depth > 0) {
152
- let kt = tokens[k][0];
153
- if (kt === jt) depth++;
154
- else if (kt === open) depth--;
155
- if (depth > 0) k--;
156
- }
157
- j = k;
199
+ j = skipBalancedPair(tokens, j - 1, jt, jt === 'CALL_END' ? 'CALL_START' : '(');
158
200
  continue;
159
201
  }
160
202
  break;
161
203
  }
162
204
  if (pt === 'CALL_END' || pt === ')') {
163
- let open = pt === 'CALL_END' ? 'CALL_START' : '(';
164
- let depth = 1;
165
- let k = j - 2;
166
- while (k >= 0 && depth > 0) {
167
- let kt = tokens[k][0];
168
- if (kt === 'CALL_END' || kt === ')') depth++;
169
- else if (kt === 'CALL_START' || kt === '(') depth--;
170
- if (depth > 0) k--;
171
- }
172
- j = k;
205
+ j = skipBalancedPair(tokens, j - 2, pt, pt === 'CALL_END' ? 'CALL_START' : '(');
206
+ continue;
207
+ }
208
+ if (pt === 'INTERPOLATION_END') {
209
+ j = skipBalancedPair(tokens, j - 2, 'INTERPOLATION_END', 'INTERPOLATION_START');
210
+ continue;
211
+ }
212
+ if (pt === 'STRING_END') {
213
+ j = skipBalancedPair(tokens, j - 2, 'STRING_END', 'STRING_START');
173
214
  continue;
174
215
  }
175
216
  j--;
@@ -1309,14 +1350,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1309
1350
  setupLines.push(` if (want === 'then') {`);
1310
1351
  setupLines.push(` currentBlock = ${thenBlockName}(this${extraArgs});`);
1311
1352
  setupLines.push(` currentBlock.c();`);
1312
- setupLines.push(` currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1353
+ setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1313
1354
  setupLines.push(` currentBlock.p(this${extraArgs});`);
1314
1355
  setupLines.push(` }`);
1315
1356
  if (elseBlock) {
1316
1357
  setupLines.push(` if (want === 'else') {`);
1317
1358
  setupLines.push(` currentBlock = ${elseBlockName}(this${extraArgs});`);
1318
1359
  setupLines.push(` currentBlock.c();`);
1319
- setupLines.push(` currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1360
+ setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1320
1361
  setupLines.push(` currentBlock.p(this${extraArgs});`);
1321
1362
  setupLines.push(` }`);
1322
1363
  }
@@ -1411,10 +1452,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1411
1452
  const condFragChildren = getFragChildren(rootVar, createLines, localizeVar);
1412
1453
  if (condFragChildren) {
1413
1454
  for (const child of condFragChildren) {
1414
- factoryLines.push(` if (detaching) ${child}.remove();`);
1455
+ factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
1415
1456
  }
1416
1457
  } else {
1417
- factoryLines.push(` if (detaching) ${localizeVar(rootVar)}.remove();`);
1458
+ factoryLines.push(` if (detaching && ${localizeVar(rootVar)}) ${localizeVar(rootVar)}.remove();`);
1418
1459
  }
1419
1460
  factoryLines.push(` }`);
1420
1461
 
@@ -1512,10 +1553,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1512
1553
  factoryLines.push(` m(target, anchor) {`);
1513
1554
  if (loopFragChildren) {
1514
1555
  for (const child of loopFragChildren) {
1515
- factoryLines.push(` target.insertBefore(${child}, anchor);`);
1556
+ factoryLines.push(` if (target) target.insertBefore(${child}, anchor);`);
1516
1557
  }
1517
1558
  } else {
1518
- factoryLines.push(` target.insertBefore(${localizeVar(itemNode)}, anchor);`);
1559
+ factoryLines.push(` if (target) target.insertBefore(${localizeVar(itemNode)}, anchor);`);
1519
1560
  }
1520
1561
  factoryLines.push(` },`);
1521
1562
 
@@ -1545,10 +1586,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1545
1586
  }
1546
1587
  if (loopFragChildren) {
1547
1588
  for (const child of loopFragChildren) {
1548
- factoryLines.push(` if (detaching) ${child}.remove();`);
1589
+ factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
1549
1590
  }
1550
1591
  } else {
1551
- factoryLines.push(` if (detaching) ${localizeVar(itemNode)}.remove();`);
1592
+ factoryLines.push(` if (detaching && ${localizeVar(itemNode)}) ${localizeVar(itemNode)}.remove();`);
1552
1593
  }
1553
1594
  factoryLines.push(` }`);
1554
1595
 
package/src/types.js CHANGED
@@ -1017,13 +1017,13 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars) {
1017
1017
  propName = isProp ? (target[2]?.valueOf?.() ?? target[2]) : (target?.valueOf?.() ?? target);
1018
1018
  type = isProp ? target[2]?.type : target?.type;
1019
1019
  hasDefault = true;
1020
- componentVars.add(propName);
1020
+ if (!isProp) componentVars.add(propName);
1021
1021
  } else if (mHead === '.') {
1022
1022
  isProp = (member[1]?.valueOf?.() ?? member[1]) === 'this';
1023
1023
  propName = isProp ? (member[2]?.valueOf?.() ?? member[2]) : null;
1024
1024
  type = isProp ? member[2]?.type : null;
1025
1025
  hasDefault = false;
1026
- if (propName) componentVars.add(propName);
1026
+ if (!isProp && propName) componentVars.add(propName);
1027
1027
  } else {
1028
1028
  continue;
1029
1029
  }
Binary file