rip-lang 3.13.25 → 3.13.27

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.25-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.27-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
14
  <a href="#"><img src="https://img.shields.io/badge/tests-1%2C300%2F1%2C300-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
@@ -354,7 +354,7 @@ The UI framework is built into rip-lang: file-based router, reactive stash, comp
354
354
  | **Self-hosting** | No | Yes |
355
355
  | **Lexer** | 3,558 LOC | 2,024 LOC |
356
356
  | **Compiler** | 10,346 LOC | 3,293 LOC |
357
- | **Total** | 17,760 LOC | ~11,300 LOC |
357
+ | **Total** | 17,760 LOC | ~11,890 LOC |
358
358
 
359
359
  Smaller codebase, modern output, built-in reactivity.
360
360
 
@@ -389,25 +389,25 @@ await rip("res = fetch! 'https://api.example.com/todos/1'; res.json!") // → {
389
389
 
390
390
  ```
391
391
  Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript
392
- (1,761) (types.js) (359) ["=", "x", 42] (3,293) + source map
392
+ (1,778) (types.js) (359) ["=", "x", 42] (3,334) + source map
393
393
  ```
394
394
 
395
395
  Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
396
396
 
397
397
  | Component | File | Lines |
398
398
  |-----------|------|-------|
399
- | Lexer + Rewriter | `src/lexer.js` | 1,761 |
400
- | Compiler + Codegen | `src/compiler.js` | 3,303 |
401
- | Type System | `src/types.js` | 1,099 |
402
- | Component System | `src/components.js` | 1,877 |
399
+ | Lexer + Rewriter | `src/lexer.js` | 1,778 |
400
+ | Compiler + Codegen | `src/compiler.js` | 3,334 |
401
+ | Type System | `src/types.js` | 1,091 |
402
+ | Component System | `src/components.js` | 2,026 |
403
403
  | Source Maps | `src/sourcemaps.js` | 189 |
404
- | Parser (generated) | `src/parser.js` | 357 |
405
- | Grammar | `src/grammar/grammar.rip` | 944 |
404
+ | Type Checking | `src/typecheck.js` | 442 |
405
+ | Parser (generated) | `src/parser.js` | 359 |
406
+ | Grammar | `src/grammar/grammar.rip` | 948 |
406
407
  | Parser Generator | `src/grammar/solar.rip` | 929 |
407
- | REPL | `src/repl.js` | 601 |
408
- | Browser Entry | `src/browser.js` | 167 |
409
- | Tags | `src/tags.js` | 62 |
410
- | **Total** | | **~11,289** |
408
+ | REPL | `src/repl.js` | 600 |
409
+ | Browser Entry | `src/browser.js` | 194 |
410
+ | **Total** | | **~11,890** |
411
411
 
412
412
  ---
413
413
 
@@ -417,7 +417,7 @@ Rip includes optional packages for full-stack development:
417
417
 
418
418
  | Package | Version | Purpose |
419
419
  |---------|---------|---------|
420
- | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.13 | Core language compiler |
420
+ | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.26 | Core language compiler |
421
421
  | [@rip-lang/server](packages/server/) | 1.2.11 | Multi-worker app server (web framework, hot reload, HTTPS, mDNS) |
422
422
  | [@rip-lang/db](packages/db/) | 1.3.13 | DuckDB server with official UI + ActiveRecord-style client |
423
423
  | [@rip-lang/grid](packages/grid/) | 0.2.8 | Reactive data grid |
@@ -462,7 +462,7 @@ rip file.rip # Run
462
462
  rip -c file.rip # Compile
463
463
  rip -t file.rip # Tokens
464
464
  rip -s file.rip # S-expressions
465
- bun run test # 1265 tests
465
+ bun run test # 1369 tests
466
466
  bun run parser # Rebuild parser
467
467
  bun run build # Build browser bundle
468
468
  ```
@@ -590,4 +590,4 @@ The browser REPL (`docs/index.html`) uses a hidden iframe as its sandbox. Code i
590
590
 
591
591
  ---
592
592
 
593
- *Rip 3.13 — 1,265 tests — Zero dependencies — Self-hosting — ~13,500 LOC*
593
+ *Rip 3.13 — 1,369 tests — Zero dependencies — Self-hosting — ~11,890 LOC*
package/docs/RIP-LANG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Rip Language Reference
4
4
 
5
- Rip is a modern reactive language that compiles to ES2022 JavaScript. It combines CoffeeScript's elegant syntax with built-in reactivity primitives. Zero dependencies, self-hosting, ~13,500 LOC.
5
+ Rip is a modern reactive language that compiles to ES2022 JavaScript. It combines CoffeeScript's elegant syntax with built-in reactivity primitives. Zero dependencies, self-hosting, ~11,890 LOC.
6
6
 
7
7
  ---
8
8
 
@@ -1829,4 +1829,4 @@ Each would need design discussion before building.
1829
1829
 
1830
1830
  ---
1831
1831
 
1832
- *Rip 3.13 — 1,265 tests — Zero dependencies — Self-hosting — ~13,500 LOC*
1832
+ *Rip 3.13 — 1,369 tests — Zero dependencies — Self-hosting — ~11,890 LOC*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.25",
3
+ "version": "3.13.27",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/compiler.js CHANGED
@@ -688,9 +688,9 @@ export class CodeGenerator {
688
688
 
689
689
  if (this.usesTemplates && !skip) {
690
690
  if (skipRT) {
691
- code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
691
+ code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
692
692
  } else if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
693
- code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
693
+ code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
694
694
  } else {
695
695
  code += this.getComponentRuntime();
696
696
  }
package/src/components.js CHANGED
@@ -55,7 +55,7 @@ const TEMPLATE_TAGS = new Set([...HTML_TAGS, ...SVG_TAGS]);
55
55
  const BIND_PREFIX = '__bind_';
56
56
  const BIND_SUFFIX = '__';
57
57
 
58
- const LIFECYCLE_HOOKS = new Set(['beforeMount', 'mounted', 'updated', 'beforeUnmount', 'unmounted']);
58
+ const LIFECYCLE_HOOKS = new Set(['beforeMount', 'mounted', 'updated', 'beforeUnmount', 'unmounted', 'onError']);
59
59
  const BOOLEAN_ATTRS = new Set([
60
60
  'disabled', 'hidden', 'readonly', 'required', 'checked', 'selected',
61
61
  'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'multiple',
@@ -240,6 +240,19 @@ export function installComponentSupport(CodeGenerator, Lexer) {
240
240
  // Only process if we're inside a render block
241
241
  if (!inRender) return 1;
242
242
 
243
+ // ─────────────────────────────────────────────────────────────────────
244
+ // Transition modifier
245
+ // div ~fade → div __transition__: "fade"
246
+ // ─────────────────────────────────────────────────────────────────────
247
+ if (tag === 'UNARY_MATH' && token[1] === '~' && nextToken && nextToken[0] === 'IDENTIFIER') {
248
+ token[0] = 'PROPERTY';
249
+ token[1] = '__transition__';
250
+ let colonToken = gen(':', ':', token);
251
+ let valueToken = gen('STRING', `"${nextToken[1]}"`, nextToken);
252
+ tokens.splice(i + 1, 1, colonToken, valueToken);
253
+ return 1;
254
+ }
255
+
243
256
  // ─────────────────────────────────────────────────────────────────────
244
257
  // Hyphenated attributes
245
258
  // data-lucide: "search" → "data-lucide": "search"
@@ -719,11 +732,12 @@ export function installComponentSupport(CodeGenerator, Lexer) {
719
732
  // --- Lifecycle hooks ---
720
733
  for (const { name, value } of lifecycleHooks) {
721
734
  if (Array.isArray(value) && (value[0] === '->' || value[0] === '=>')) {
722
- const [, , hookBody] = value;
735
+ const [, params, hookBody] = value;
736
+ const paramStr = Array.isArray(params) ? params.map(p => this.formatParam(p)).join(', ') : '';
723
737
  const transformed = this.reactiveMembers ? this.transformComponentMembers(hookBody) : hookBody;
724
738
  const isAsync = this.containsAwait(hookBody);
725
- const bodyCode = this.generateFunctionBody(transformed, []);
726
- lines.push(` ${isAsync ? 'async ' : ''}${name}() ${bodyCode}`);
739
+ const bodyCode = this.generateFunctionBody(transformed, params || []);
740
+ lines.push(` ${isAsync ? 'async ' : ''}${name}(${paramStr}) ${bodyCode}`);
727
741
  }
728
742
  }
729
743
 
@@ -1188,6 +1202,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1188
1202
  continue;
1189
1203
  }
1190
1204
 
1205
+ // Transition: __transition__: "fade" → this._t = "fade" (on block, not DOM)
1206
+ if (key === '__transition__') {
1207
+ const transName = String(value).replace(/^["']|["']$/g, '');
1208
+ this._createLines.push(`this._t = "${transName}";`);
1209
+ continue;
1210
+ }
1211
+
1191
1212
  // Element ref: ref: "name" → this.name = element
1192
1213
  if (key === 'ref') {
1193
1214
  const refName = String(value).replace(/^["']|["']$/g, '');
@@ -1330,7 +1351,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1330
1351
  setupLines.push(` if (want === showing) return;`);
1331
1352
  setupLines.push(``);
1332
1353
  setupLines.push(` if (currentBlock) {`);
1333
- setupLines.push(` currentBlock.d(true);`);
1354
+ setupLines.push(` const leaving = currentBlock;`);
1355
+ setupLines.push(` if (leaving._t) { __transition(leaving._first, leaving._t, 'leave', () => leaving.d(true)); }`);
1356
+ setupLines.push(` else { leaving.d(true); }`);
1334
1357
  setupLines.push(` currentBlock = null;`);
1335
1358
  setupLines.push(` }`);
1336
1359
  setupLines.push(` showing = want;`);
@@ -1340,6 +1363,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1340
1363
  setupLines.push(` currentBlock.c();`);
1341
1364
  setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1342
1365
  setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
1366
+ setupLines.push(` if (currentBlock._t) __transition(currentBlock._first, currentBlock._t, 'enter');`);
1343
1367
  setupLines.push(` }`);
1344
1368
  if (elseBlock) {
1345
1369
  setupLines.push(` if (want === 'else') {`);
@@ -1347,6 +1371,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1347
1371
  setupLines.push(` currentBlock.c();`);
1348
1372
  setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1349
1373
  setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
1374
+ setupLines.push(` if (currentBlock._t) __transition(currentBlock._first, currentBlock._t, 'enter');`);
1350
1375
  setupLines.push(` }`);
1351
1376
  }
1352
1377
  setupLines.push(` ${effClose}`);
@@ -1553,8 +1578,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1553
1578
  this._createLines.push(`${elVar} = ${instVar}._create();`);
1554
1579
  this._createLines.push(`(${s}._children || (${s}._children = [])).push(${instVar});`);
1555
1580
 
1556
- this._setupLines.push(`if (${instVar}._setup) ${instVar}._setup();`);
1557
- this._setupLines.push(`if (${instVar}.mounted) ${instVar}.mounted();`);
1581
+ this._setupLines.push(`try { if (${instVar}._setup) ${instVar}._setup(); if (${instVar}.mounted) ${instVar}.mounted(); } catch (__e) { __handleComponentError(__e, ${instVar}); }`);
1558
1582
 
1559
1583
  for (const { key, valueCode } of reactiveProps) {
1560
1584
  this._pushEffect(`if (${instVar}.${key}) ${instVar}.${key}.value = ${valueCode};`);
@@ -1904,21 +1928,75 @@ function __reconcile(anchor, state, items, ctx, factory, keyFn, ...outer) {
1904
1928
  state.blocks = newBlocks;
1905
1929
  }
1906
1930
 
1931
+ let __cssInjected = false;
1932
+ function __transitionCSS() {
1933
+ if (__cssInjected) return;
1934
+ __cssInjected = true;
1935
+ const s = document.createElement('style');
1936
+ s.textContent = [
1937
+ '.fade-enter-active,.fade-leave-active{transition:opacity .2s ease}',
1938
+ '.fade-enter-from,.fade-leave-to{opacity:0}',
1939
+ '.slide-enter-active,.slide-leave-active{transition:opacity .2s ease,transform .2s ease}',
1940
+ '.slide-enter-from{opacity:0;transform:translateY(-8px)}',
1941
+ '.slide-leave-to{opacity:0;transform:translateY(8px)}',
1942
+ '.scale-enter-active,.scale-leave-active{transition:opacity .2s ease,transform .2s ease}',
1943
+ '.scale-enter-from,.scale-leave-to{opacity:0;transform:scale(.95)}',
1944
+ '.blur-enter-active,.blur-leave-active{transition:opacity .2s ease,filter .2s ease}',
1945
+ '.blur-enter-from,.blur-leave-to{opacity:0;filter:blur(4px)}',
1946
+ '.fly-enter-active,.fly-leave-active{transition:opacity .2s ease,transform .2s ease}',
1947
+ '.fly-enter-from{opacity:0;transform:translateY(-20px)}',
1948
+ '.fly-leave-to{opacity:0;transform:translateY(20px)}',
1949
+ ].join('');
1950
+ document.head.appendChild(s);
1951
+ }
1952
+
1953
+ function __transition(el, name, dir, done) {
1954
+ __transitionCSS();
1955
+ const cl = el.classList;
1956
+ const from = name + '-' + dir + '-from';
1957
+ const active = name + '-' + dir + '-active';
1958
+ const to = name + '-' + dir + '-to';
1959
+ cl.add(from, active);
1960
+ requestAnimationFrame(() => {
1961
+ requestAnimationFrame(() => {
1962
+ cl.remove(from);
1963
+ cl.add(to);
1964
+ const end = () => { cl.remove(active, to); if (done) done(); };
1965
+ el.addEventListener('transitionend', end, { once: true });
1966
+ });
1967
+ });
1968
+ }
1969
+
1970
+ function __handleComponentError(error, component) {
1971
+ let current = component;
1972
+ while (current) {
1973
+ if (current.onError) {
1974
+ try { current.onError(error, component); return; } catch (_) {}
1975
+ }
1976
+ current = current._parent;
1977
+ }
1978
+ throw error;
1979
+ }
1980
+
1907
1981
  class __Component {
1908
1982
  constructor(props = {}) {
1909
1983
  Object.assign(this, props);
1910
1984
  const prev = __pushComponent(this);
1911
- this._init(props);
1985
+ try { this._init(props); } catch (e) { __popComponent(prev); __handleComponentError(e, this); return; }
1912
1986
  __popComponent(prev);
1913
1987
  }
1914
1988
  _init() {}
1915
1989
  mount(target) {
1916
1990
  if (typeof target === "string") target = document.querySelector(target);
1917
1991
  this._target = target;
1918
- this._root = this._create();
1919
- target.appendChild(this._root);
1920
- if (this._setup) this._setup();
1921
- if (this.mounted) this.mounted();
1992
+ try {
1993
+ this._root = this._create();
1994
+ target.appendChild(this._root);
1995
+ if (this._setup) this._setup();
1996
+ if (this.mounted) this.mounted();
1997
+ } catch (error) {
1998
+ __handleComponentError(error, this);
1999
+ }
1922
2000
  return this;
1923
2001
  }
1924
2002
  unmount() {
@@ -1939,7 +2017,7 @@ class __Component {
1939
2017
 
1940
2018
  // Register on globalThis for runtime deduplication
1941
2019
  if (typeof globalThis !== 'undefined') {
1942
- globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component };
2020
+ globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component };
1943
2021
  }
1944
2022
 
1945
2023
  `;