rip-lang 3.13.26 → 3.13.28
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 +25 -24
- package/bin/rip +29 -18
- package/docs/RIP-LANG.md +259 -4
- package/docs/RIP-TYPES.md +1 -1
- package/docs/dist/rip.js +414 -269
- package/docs/dist/rip.min.js +359 -178
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/app.rip +1 -1
- package/src/compiler.js +28 -2
- package/src/components.js +25 -13
- package/src/grammar/grammar.rip +6 -3
- package/src/lexer.js +15 -10
- package/src/parser.js +28 -27
- package/docs/RIP-INTERNALS.md +0 -593
- package/docs/WEB-FRAMEWORKS.md +0 -486
- package/docs/WEB-STYLING.md +0 -701
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.
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.28-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>
|
|
@@ -205,6 +205,7 @@ All use `globalThis` with `??=` — override any by redeclaring locally.
|
|
|
205
205
|
| `!` (void) | `def process!` | Suppresses implicit return |
|
|
206
206
|
| `!?` (otherwise) | `val !? 5` | Default only if `undefined` (infix) |
|
|
207
207
|
| `!?` (defined) | `val!?` | True if not `undefined` (postfix) |
|
|
208
|
+
| `?!` (presence) | `@checked?!` | True if truthy, else `undefined` (Houdini operator) |
|
|
208
209
|
| `?` (existence) | `x?` | True if `x != null` |
|
|
209
210
|
| `?:` (ternary) | `x > 0 ? 'yes' : 'no'` | JS-style ternary expression |
|
|
210
211
|
| `if...else` (postfix) | `"yes" if cond else "no"` | Python-style ternary expression |
|
|
@@ -354,7 +355,7 @@ The UI framework is built into rip-lang: file-based router, reactive stash, comp
|
|
|
354
355
|
| **Self-hosting** | No | Yes |
|
|
355
356
|
| **Lexer** | 3,558 LOC | 2,024 LOC |
|
|
356
357
|
| **Compiler** | 10,346 LOC | 3,293 LOC |
|
|
357
|
-
| **Total** | 17,760 LOC | ~11,
|
|
358
|
+
| **Total** | 17,760 LOC | ~11,890 LOC |
|
|
358
359
|
|
|
359
360
|
Smaller codebase, modern output, built-in reactivity.
|
|
360
361
|
|
|
@@ -389,25 +390,25 @@ await rip("res = fetch! 'https://api.example.com/todos/1'; res.json!") // → {
|
|
|
389
390
|
|
|
390
391
|
```
|
|
391
392
|
Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript
|
|
392
|
-
(1,
|
|
393
|
+
(1,778) (types.js) (359) ["=", "x", 42] (3,334) + source map
|
|
393
394
|
```
|
|
394
395
|
|
|
395
396
|
Simple arrays (with `.loc`) instead of AST node classes. The compiler is self-hosting — `bun run parser` rebuilds from source.
|
|
396
397
|
|
|
397
398
|
| Component | File | Lines |
|
|
398
399
|
|-----------|------|-------|
|
|
399
|
-
| Lexer + Rewriter | `src/lexer.js` | 1,
|
|
400
|
-
| Compiler + Codegen | `src/compiler.js` | 3,
|
|
401
|
-
| Type System | `src/types.js` | 1,
|
|
402
|
-
| Component System | `src/components.js` |
|
|
400
|
+
| Lexer + Rewriter | `src/lexer.js` | 1,778 |
|
|
401
|
+
| Compiler + Codegen | `src/compiler.js` | 3,334 |
|
|
402
|
+
| Type System | `src/types.js` | 1,091 |
|
|
403
|
+
| Component System | `src/components.js` | 2,026 |
|
|
403
404
|
| Source Maps | `src/sourcemaps.js` | 189 |
|
|
404
|
-
|
|
|
405
|
-
|
|
|
405
|
+
| Type Checking | `src/typecheck.js` | 442 |
|
|
406
|
+
| Parser (generated) | `src/parser.js` | 359 |
|
|
407
|
+
| Grammar | `src/grammar/grammar.rip` | 948 |
|
|
406
408
|
| Parser Generator | `src/grammar/solar.rip` | 929 |
|
|
407
|
-
| REPL | `src/repl.js` |
|
|
408
|
-
| Browser Entry | `src/browser.js` |
|
|
409
|
-
|
|
|
410
|
-
| **Total** | | **~11,289** |
|
|
409
|
+
| REPL | `src/repl.js` | 600 |
|
|
410
|
+
| Browser Entry | `src/browser.js` | 194 |
|
|
411
|
+
| **Total** | | **~11,890** |
|
|
411
412
|
|
|
412
413
|
---
|
|
413
414
|
|
|
@@ -417,14 +418,14 @@ Rip includes optional packages for full-stack development:
|
|
|
417
418
|
|
|
418
419
|
| Package | Version | Purpose |
|
|
419
420
|
|---------|---------|---------|
|
|
420
|
-
| [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.
|
|
421
|
-
| [@rip-lang/server](packages/server/) | 1.
|
|
422
|
-
| [@rip-lang/db](packages/db/) | 1.3.
|
|
423
|
-
| [@rip-lang/grid](packages/grid/) | 0.2.
|
|
424
|
-
| [@rip-lang/swarm](packages/swarm/) | 1.2.
|
|
425
|
-
| [@rip-lang/csv](packages/csv/) | 1.3.
|
|
426
|
-
| [@rip-lang/schema](packages/schema/) | 0.3.
|
|
427
|
-
| [VS Code Extension](packages/vscode/) | 0.5.
|
|
421
|
+
| [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.27 | Core language compiler |
|
|
422
|
+
| [@rip-lang/server](packages/server/) | 1.3.12 | Multi-worker app server (web framework, hot reload, HTTPS, mDNS) |
|
|
423
|
+
| [@rip-lang/db](packages/db/) | 1.3.15 | DuckDB server with official UI + ActiveRecord-style client |
|
|
424
|
+
| [@rip-lang/grid](packages/grid/) | 0.2.10 | Reactive data grid |
|
|
425
|
+
| [@rip-lang/swarm](packages/swarm/) | 1.2.18 | Parallel job runner with worker pool |
|
|
426
|
+
| [@rip-lang/csv](packages/csv/) | 1.3.6 | CSV parser + writer |
|
|
427
|
+
| [@rip-lang/schema](packages/schema/) | 0.3.8 | Unified schema → TypeScript types, SQL DDL, validation, ORM |
|
|
428
|
+
| [VS Code Extension](packages/vscode/) | 0.5.7 | Syntax highlighting, type intelligence, source maps |
|
|
428
429
|
|
|
429
430
|
```bash
|
|
430
431
|
bun add -g @rip-lang/db # Installs everything (rip-lang + server + db)
|
|
@@ -462,7 +463,7 @@ rip file.rip # Run
|
|
|
462
463
|
rip -c file.rip # Compile
|
|
463
464
|
rip -t file.rip # Tokens
|
|
464
465
|
rip -s file.rip # S-expressions
|
|
465
|
-
bun run test #
|
|
466
|
+
bun run test # 1369 tests
|
|
466
467
|
bun run parser # Rebuild parser
|
|
467
468
|
bun run build # Build browser bundle
|
|
468
469
|
```
|
|
@@ -473,9 +474,9 @@ bun run build # Build browser bundle
|
|
|
473
474
|
|
|
474
475
|
| Guide | Description |
|
|
475
476
|
|-------|-------------|
|
|
476
|
-
| [docs/RIP-LANG.md](docs/RIP-LANG.md) | Full language reference (syntax, operators, reactivity, types,
|
|
477
|
-
| [docs/RIP-INTERNALS.md](docs/RIP-INTERNALS.md) | Compiler architecture (lexer, parser, codegen, S-expressions) |
|
|
477
|
+
| [docs/RIP-LANG.md](docs/RIP-LANG.md) | Full language reference (syntax, operators, reactivity, types, components) |
|
|
478
478
|
| [docs/RIP-TYPES.md](docs/RIP-TYPES.md) | Type system specification |
|
|
479
|
+
| [AGENTS.md](AGENTS.md) | Compiler architecture, S-expressions, component system internals |
|
|
479
480
|
| [AGENTS.md](AGENTS.md) | AI agents — get up to speed for working on the compiler |
|
|
480
481
|
|
|
481
482
|
---
|
package/bin/rip
CHANGED
|
@@ -69,6 +69,7 @@ Examples:
|
|
|
69
69
|
rip -m example.rip # Compile with inline source map
|
|
70
70
|
rip -cd example.rip # Show compiled JS and type declarations
|
|
71
71
|
rip -q -c example.rip # Just the JS, no headers (for piping)
|
|
72
|
+
echo 'p 1 + 2' | rip # Execute from stdin
|
|
72
73
|
echo 'x = 1 + 2' | rip -c # Compile from stdin
|
|
73
74
|
|
|
74
75
|
Shebang support:
|
|
@@ -172,14 +173,29 @@ async function main() {
|
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
//
|
|
176
|
+
// Stdin: write to temp .rip file so it flows through the normal execution/compile paths
|
|
176
177
|
const hasCompileFlag = showCompiled || showTokens || showSExpr || generateDts || generateMap || outputFile;
|
|
177
|
-
|
|
178
|
+
let isStdin = false;
|
|
179
|
+
if (!inputFile) {
|
|
180
|
+
const tmp = join(process.cwd(), '.__rip_stdin__.rip');
|
|
181
|
+
try {
|
|
182
|
+
writeFileSync(tmp, readFileSync(0, 'utf-8'));
|
|
183
|
+
inputFile = tmp;
|
|
184
|
+
isStdin = true;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Error reading stdin:', error.message);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Execute script directly (no compile flags)
|
|
192
|
+
if (!hasCompileFlag && isFile(inputFile)) {
|
|
178
193
|
if (inputFile.endsWith('.rip')) {
|
|
179
194
|
const result = spawnSync('bun', ['--preload', loaderPath, inputFile, ...scriptArgs], {
|
|
180
195
|
stdio: 'inherit',
|
|
181
196
|
env: process.env
|
|
182
197
|
});
|
|
198
|
+
if (isStdin) try { unlinkSync(inputFile); } catch {}
|
|
183
199
|
process.exit(result.status ?? 1);
|
|
184
200
|
}
|
|
185
201
|
|
|
@@ -201,7 +217,7 @@ async function main() {
|
|
|
201
217
|
|
|
202
218
|
// Fallback: look for bin/{command} in git repo root
|
|
203
219
|
// Allows `rip migrate --status` to find and run {repo}/bin/migrate
|
|
204
|
-
if (inputFile && !inputFile.startsWith('-') && !isFile(inputFile)) {
|
|
220
|
+
if (!isStdin && inputFile && !inputFile.startsWith('-') && !isFile(inputFile)) {
|
|
205
221
|
try {
|
|
206
222
|
const repoRoot = execSync('git rev-parse --show-toplevel', {
|
|
207
223
|
encoding: 'utf-8',
|
|
@@ -215,33 +231,26 @@ async function main() {
|
|
|
215
231
|
} catch {}
|
|
216
232
|
}
|
|
217
233
|
|
|
218
|
-
// --- Compile ---
|
|
234
|
+
// --- Compile (with flags) ---
|
|
219
235
|
|
|
220
|
-
|
|
236
|
+
if (!isFile(inputFile)) {
|
|
237
|
+
console.error(`Error: File not found: ${inputFile}`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
221
240
|
|
|
222
|
-
|
|
223
|
-
if (inputFile) {
|
|
224
|
-
if (!isFile(inputFile)) {
|
|
225
|
-
console.error(`Error: File not found: ${inputFile}`);
|
|
226
|
-
process.exit(1);
|
|
227
|
-
}
|
|
228
|
-
source = readFileSync(inputFile, 'utf-8');
|
|
229
|
-
} else {
|
|
230
|
-
source = readFileSync(0, 'utf-8');
|
|
231
|
-
}
|
|
241
|
+
let source = readFileSync(inputFile, 'utf-8');
|
|
232
242
|
|
|
243
|
+
try {
|
|
233
244
|
const compiler = new Compiler({ showTokens, showSExpr, quiet,
|
|
234
245
|
types: generateDts ? 'emit' : undefined,
|
|
235
246
|
sourceMap: generateMap ? 'inline' : undefined,
|
|
236
247
|
});
|
|
237
248
|
const result = compiler.compile(source);
|
|
238
249
|
|
|
239
|
-
const shouldShowCompiled = showCompiled || (!showTokens && !showSExpr && !generateDts) || outputFile;
|
|
240
|
-
|
|
241
250
|
if (outputFile) {
|
|
242
251
|
writeFileSync(outputFile, result.code, 'utf-8');
|
|
243
252
|
if (!quiet) console.log(`Compiled to ${outputFile}`);
|
|
244
|
-
} else if (
|
|
253
|
+
} else if (showCompiled) {
|
|
245
254
|
if (!quiet) console.log(`// == JavaScript output by Rip ${VERSION} == //\n`);
|
|
246
255
|
console.log(result.code);
|
|
247
256
|
}
|
|
@@ -254,6 +263,8 @@ async function main() {
|
|
|
254
263
|
console.error('Compilation Error:', error.message);
|
|
255
264
|
if (error.stack) console.error(error.stack);
|
|
256
265
|
process.exit(1);
|
|
266
|
+
} finally {
|
|
267
|
+
if (isStdin) try { unlinkSync(inputFile); } catch {}
|
|
257
268
|
}
|
|
258
269
|
}
|
|
259
270
|
|
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, ~
|
|
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
|
|
|
@@ -295,6 +295,7 @@ Multiple lines
|
|
|
295
295
|
| `of` | `k of obj` | Object key existence |
|
|
296
296
|
| `?` (postfix) | `a?` | Existence check (`a != null`) |
|
|
297
297
|
| `!?` (postfix) | `a!?` | Defined check (`a !== undefined`) |
|
|
298
|
+
| `?!` (postfix) | `a?!` | Presence check — true if truthy, else undefined |
|
|
298
299
|
| `?` (ternary) | `a ? b : c` | Ternary conditional |
|
|
299
300
|
| `if...else` (postfix) | `b if a else c` | Python-style ternary |
|
|
300
301
|
| `?.` `?.[]` `?.()` | `a?.b` `a?.[0]` `a?.()` | Optional chaining (ES6) |
|
|
@@ -315,6 +316,7 @@ Multiple lines
|
|
|
315
316
|
| `!` | Void | `def process!` | Suppresses implicit return |
|
|
316
317
|
| `!?` | Otherwise | `val !? 5` | Default if undefined (infix) |
|
|
317
318
|
| `!?` | Defined | `val!?` | True if not undefined (postfix) |
|
|
319
|
+
| `?!` | Presence | `@checked?!` | `(this.checked ? true : undefined)` — Houdini operator |
|
|
318
320
|
| `=~` | Match | `str =~ /pat/` | Ruby-style regex match, captures in `_` |
|
|
319
321
|
| `::` | Prototype | `String::trim` | `String.prototype.trim` |
|
|
320
322
|
| `[-n]` | Negative index | `arr[-1]` | `arr.at(-1)` |
|
|
@@ -404,6 +406,40 @@ The postfix form mirrors `?` (existence check) but with tighter semantics:
|
|
|
404
406
|
|----------|--------|--------|-------------|-----|---------|------|
|
|
405
407
|
| `v?` | not nullish | false | false | true | true | true |
|
|
406
408
|
| `v!?` | not undefined | true | false | true | true | true |
|
|
409
|
+
| `v?!` | truthy presence | `undefined` | `undefined` | `undefined` | `undefined` | `undefined` |
|
|
410
|
+
|
|
411
|
+
Note: `v?!` returns `true` for truthy values, `undefined` for falsy values.
|
|
412
|
+
|
|
413
|
+
## Presence Operator (`?!`) — The Houdini
|
|
414
|
+
|
|
415
|
+
The `?!` operator (postfix, unspaced) returns `true` if the value is truthy,
|
|
416
|
+
or `undefined` if it's falsy. Now you see it… now you don't.
|
|
417
|
+
|
|
418
|
+
```coffee
|
|
419
|
+
@checked?! # (this.checked ? true : undefined)
|
|
420
|
+
(idx is active)?! # ((idx === active) ? true : undefined)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Designed for `data-*` attributes in headless UI components, where falsy values
|
|
424
|
+
need to *remove* the attribute rather than set it to `"false"`:
|
|
425
|
+
|
|
426
|
+
```coffee
|
|
427
|
+
# Before — verbose and repetitive
|
|
428
|
+
data-checked: (@checked or undefined),
|
|
429
|
+
data-disabled: (@disabled or undefined),
|
|
430
|
+
|
|
431
|
+
# After — clean and expressive
|
|
432
|
+
data-checked: @checked?!,
|
|
433
|
+
data-disabled: @disabled?!,
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Works with any expression, not just identifiers:
|
|
437
|
+
|
|
438
|
+
```coffee
|
|
439
|
+
data-highlighted: (idx is highlightedIndex)?!,
|
|
440
|
+
data-selected: (opt.value is String(@value))?!,
|
|
441
|
+
data-active: (tab is @active)?!,
|
|
442
|
+
```
|
|
407
443
|
|
|
408
444
|
## Method Assignment (`.=`)
|
|
409
445
|
|
|
@@ -1337,6 +1373,225 @@ Counter = component
|
|
|
1337
1373
|
button @click: @increment, "+"
|
|
1338
1374
|
```
|
|
1339
1375
|
|
|
1376
|
+
### Component Features
|
|
1377
|
+
|
|
1378
|
+
**State and Computed:**
|
|
1379
|
+
|
|
1380
|
+
```coffee
|
|
1381
|
+
App = component
|
|
1382
|
+
count := 0 # reactive state
|
|
1383
|
+
doubled ~= count * 2 # computed (auto-updates)
|
|
1384
|
+
label =! "Counter" # readonly (const)
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
**Public Props (passed from parent):**
|
|
1388
|
+
|
|
1389
|
+
```coffee
|
|
1390
|
+
Card = component
|
|
1391
|
+
@title =! "Untitled" # readonly prop with default
|
|
1392
|
+
@count := 0 # reactive prop (two-way capable)
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
```coffee
|
|
1396
|
+
# Parent passes props
|
|
1397
|
+
Card title: "Hello", count: 42
|
|
1398
|
+
```
|
|
1399
|
+
|
|
1400
|
+
**Methods:**
|
|
1401
|
+
|
|
1402
|
+
```coffee
|
|
1403
|
+
App = component
|
|
1404
|
+
count := 0
|
|
1405
|
+
inc = -> @count += 1
|
|
1406
|
+
add = (n) -> @count += n
|
|
1407
|
+
```
|
|
1408
|
+
|
|
1409
|
+
**Lifecycle Hooks:**
|
|
1410
|
+
|
|
1411
|
+
```coffee
|
|
1412
|
+
App = component
|
|
1413
|
+
beforeMount = -> p "about to mount"
|
|
1414
|
+
mounted = -> p "mounted"
|
|
1415
|
+
updated = -> p "updated"
|
|
1416
|
+
beforeUnmount = -> p "about to unmount"
|
|
1417
|
+
unmounted = -> p "unmounted"
|
|
1418
|
+
onError = (err, comp) -> p "caught: #{err.message}"
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
**Effects:**
|
|
1422
|
+
|
|
1423
|
+
```coffee
|
|
1424
|
+
App = component
|
|
1425
|
+
count := 0
|
|
1426
|
+
~> p "count is now #{count}" # re-runs when count changes
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
**Render Blocks — Template Syntax:**
|
|
1430
|
+
|
|
1431
|
+
```coffee
|
|
1432
|
+
App = component
|
|
1433
|
+
name := "world"
|
|
1434
|
+
render
|
|
1435
|
+
div.card # element with class
|
|
1436
|
+
h1#title "Hello" # element with id
|
|
1437
|
+
span name # reactive text
|
|
1438
|
+
input value <=> name # two-way binding
|
|
1439
|
+
button @click: -> @name = "Rip"
|
|
1440
|
+
"Click me"
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
**Conditional Rendering:**
|
|
1444
|
+
|
|
1445
|
+
```coffee
|
|
1446
|
+
App = component
|
|
1447
|
+
show := true
|
|
1448
|
+
render
|
|
1449
|
+
if show
|
|
1450
|
+
div "Visible!"
|
|
1451
|
+
else
|
|
1452
|
+
div "Hidden"
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
**List Rendering:**
|
|
1456
|
+
|
|
1457
|
+
```coffee
|
|
1458
|
+
App = component
|
|
1459
|
+
items := ["Apple", "Banana", "Cherry"]
|
|
1460
|
+
render
|
|
1461
|
+
ul
|
|
1462
|
+
for item, i in items
|
|
1463
|
+
li item
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
Lists use keyed reconciliation with LIS (Longest Increasing Subsequence) diffing — only items that actually move get repositioned. Appending to a list is nearly zero-cost.
|
|
1467
|
+
|
|
1468
|
+
**Child Components and Slots:**
|
|
1469
|
+
|
|
1470
|
+
```coffee
|
|
1471
|
+
Card = component
|
|
1472
|
+
@title =! "Card"
|
|
1473
|
+
@children =! null
|
|
1474
|
+
render
|
|
1475
|
+
div.card
|
|
1476
|
+
h2 @title
|
|
1477
|
+
@children
|
|
1478
|
+
|
|
1479
|
+
App = component
|
|
1480
|
+
render
|
|
1481
|
+
Card title: "Welcome"
|
|
1482
|
+
p "This is the card body"
|
|
1483
|
+
```
|
|
1484
|
+
|
|
1485
|
+
**CSS Transitions:**
|
|
1486
|
+
|
|
1487
|
+
Add `~name` to any element inside a conditional block for enter/leave animations:
|
|
1488
|
+
|
|
1489
|
+
```coffee
|
|
1490
|
+
App = component
|
|
1491
|
+
show := true
|
|
1492
|
+
render
|
|
1493
|
+
button @click: -> @show = !show
|
|
1494
|
+
"Toggle"
|
|
1495
|
+
if show
|
|
1496
|
+
div ~fade
|
|
1497
|
+
p "I fade in and out"
|
|
1498
|
+
```
|
|
1499
|
+
|
|
1500
|
+
Built-in presets:
|
|
1501
|
+
|
|
1502
|
+
| Preset | Effect |
|
|
1503
|
+
|--------|--------|
|
|
1504
|
+
| `~fade` | Opacity fade |
|
|
1505
|
+
| `~slide` | Slide up/down with opacity |
|
|
1506
|
+
| `~scale` | Scale from 95% with opacity |
|
|
1507
|
+
| `~blur` | Blur with opacity |
|
|
1508
|
+
| `~fly` | Fly in/out from distance with opacity |
|
|
1509
|
+
|
|
1510
|
+
Custom transitions work with any name — just provide your own CSS:
|
|
1511
|
+
|
|
1512
|
+
```css
|
|
1513
|
+
.wobble-enter-active, .wobble-leave-active { transition: transform 0.3s ease; }
|
|
1514
|
+
.wobble-enter-from { transform: rotate(-5deg); }
|
|
1515
|
+
.wobble-leave-to { transform: rotate(5deg); }
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
```coffee
|
|
1519
|
+
div ~wobble
|
|
1520
|
+
span "Custom animation"
|
|
1521
|
+
```
|
|
1522
|
+
|
|
1523
|
+
**Error Boundaries:**
|
|
1524
|
+
|
|
1525
|
+
The `onError` lifecycle hook catches errors from child components:
|
|
1526
|
+
|
|
1527
|
+
```coffee
|
|
1528
|
+
App = component
|
|
1529
|
+
onError = (err, source) ->
|
|
1530
|
+
p "Error in #{source}: #{err.message}"
|
|
1531
|
+
render
|
|
1532
|
+
ChildThatMightFail
|
|
1533
|
+
```
|
|
1534
|
+
|
|
1535
|
+
Errors walk up the component tree (`_parent` chain) to the nearest `onError` handler. Without a boundary, errors throw normally.
|
|
1536
|
+
|
|
1537
|
+
**Context API:**
|
|
1538
|
+
|
|
1539
|
+
Share data across the component tree without prop drilling:
|
|
1540
|
+
|
|
1541
|
+
```coffee
|
|
1542
|
+
ThemeProvider = component
|
|
1543
|
+
~> setContext "theme", "dark"
|
|
1544
|
+
|
|
1545
|
+
ThemedButton = component
|
|
1546
|
+
theme =! getContext "theme"
|
|
1547
|
+
render
|
|
1548
|
+
button class: theme
|
|
1549
|
+
```
|
|
1550
|
+
|
|
1551
|
+
**SVG Rendering:**
|
|
1552
|
+
|
|
1553
|
+
SVG elements use `createElementNS` automatically:
|
|
1554
|
+
|
|
1555
|
+
```coffee
|
|
1556
|
+
Icon = component
|
|
1557
|
+
render
|
|
1558
|
+
svg.icon
|
|
1559
|
+
path d: "M10 10 L20 20"
|
|
1560
|
+
circle cx: "50", cy: "50", r: "40"
|
|
1561
|
+
```
|
|
1562
|
+
|
|
1563
|
+
**Dynamic Classes:**
|
|
1564
|
+
|
|
1565
|
+
```coffee
|
|
1566
|
+
App = component
|
|
1567
|
+
isActive := true
|
|
1568
|
+
render
|
|
1569
|
+
div.card class: (isActive && "active")
|
|
1570
|
+
div.("card", isActive && "active") # .() syntax
|
|
1571
|
+
```
|
|
1572
|
+
|
|
1573
|
+
**Hyphenated Attributes:**
|
|
1574
|
+
|
|
1575
|
+
```coffee
|
|
1576
|
+
div data-testid: "main", aria-label: "content"
|
|
1577
|
+
```
|
|
1578
|
+
|
|
1579
|
+
**DOM Properties:**
|
|
1580
|
+
|
|
1581
|
+
```coffee
|
|
1582
|
+
div innerHTML: content # reactive innerHTML
|
|
1583
|
+
div textContent: text # reactive textContent
|
|
1584
|
+
```
|
|
1585
|
+
|
|
1586
|
+
**Element Refs:**
|
|
1587
|
+
|
|
1588
|
+
```coffee
|
|
1589
|
+
App = component
|
|
1590
|
+
render
|
|
1591
|
+
canvas ref: "canvas"
|
|
1592
|
+
mounted = -> p @canvas # access the DOM element
|
|
1593
|
+
```
|
|
1594
|
+
|
|
1340
1595
|
## @rip-lang/db — DuckDB Server + Client
|
|
1341
1596
|
|
|
1342
1597
|
HTTP server for DuckDB with the official DuckDB UI built in, plus an ActiveRecord-style client library.
|
|
@@ -1733,6 +1988,7 @@ X.new(a: 1)
|
|
|
1733
1988
|
a! # await a()
|
|
1734
1989
|
a !? b # a if defined, else b (infix otherwise)
|
|
1735
1990
|
a!? # true if a is defined (postfix defined check)
|
|
1991
|
+
a?! # true if truthy, else undefined (Houdini)
|
|
1736
1992
|
a // b # floor divide
|
|
1737
1993
|
a %% b # true modulo
|
|
1738
1994
|
a =~ /pat/ # regex match, captures in _
|
|
@@ -1824,9 +2080,8 @@ Each would need design discussion before building.
|
|
|
1824
2080
|
|----------|---------|
|
|
1825
2081
|
| **[RIP-LANG.md](RIP-LANG.md)** | Full language reference (this file) |
|
|
1826
2082
|
| **[RIP-TYPES.md](RIP-TYPES.md)** | Type system specification |
|
|
1827
|
-
| **[
|
|
1828
|
-
| **[AGENTS.md](../AGENTS.md)** | AI agent guide for working on the compiler |
|
|
2083
|
+
| **[AGENTS.md](../AGENTS.md)** | Compiler architecture, S-expressions, AI agent guide |
|
|
1829
2084
|
|
|
1830
2085
|
---
|
|
1831
2086
|
|
|
1832
|
-
*Rip 3.13 — 1,
|
|
2087
|
+
*Rip 3.13 — 1,369 tests — Zero dependencies — Self-hosting — ~11,890 LOC*
|
package/docs/RIP-TYPES.md
CHANGED
|
@@ -1978,4 +1978,4 @@ The type system uses a two-phase approach:
|
|
|
1978
1978
|
|
|
1979
1979
|
**See Also:**
|
|
1980
1980
|
- [RIP-LANG.md](RIP-LANG.md) — Language reference (includes reactivity and types)
|
|
1981
|
-
- [
|
|
1981
|
+
- [AGENTS.md](../AGENTS.md) — Compiler architecture, S-expressions, design decisions
|