rip-lang 3.13.55 → 3.13.56

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.55-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.56-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>
@@ -105,6 +105,7 @@ def loadUser(id)
105
105
  await response.json()
106
106
 
107
107
  user?.profile?.name # Optional chaining
108
+ el?.scrollTop = 0 # Optional chain assignment
108
109
  data = fetchData! # Await shorthand
109
110
  ```
110
111
 
@@ -211,6 +212,7 @@ All use `globalThis` with `??=` — override any by redeclaring locally.
211
212
  | `if...else` (postfix) | `"yes" if cond else "no"` | Python-style ternary expression |
212
213
  | `?.` `?.[]` `?.()` | `a?.b` `a?.[0]` `a?.()` | Optional chaining (ES6) |
213
214
  | `?[]` `?()` | `a?[0]` `a?(x)` | Optional chaining shorthand |
215
+ | `?.` `=` | `el?.scrollTop = 0` | Optional chain assignment — guarded write |
214
216
  | `??` | `a ?? b` | Nullish coalescing |
215
217
  | `...` (spread) | `[...items, last]` | Prefix spread (ES6) |
216
218
  | `//` | `7 // 2` | Floor division |
@@ -421,7 +423,7 @@ Rip includes optional packages for full-stack development:
421
423
  | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.13.27 | Core language compiler |
422
424
  | [@rip-lang/server](packages/server/) | 1.3.12 | Multi-worker app server (web framework, hot reload, HTTPS, mDNS) |
423
425
  | [@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 |
426
+ | [ui](packages/ui/) | | Rip UI 38 headless components (Grid, Select, Dialog, Tabs, etc.) |
425
427
  | [@rip-lang/swarm](packages/swarm/) | 1.2.18 | Parallel job runner with worker pool |
426
428
  | [@rip-lang/csv](packages/csv/) | 1.3.6 | CSV parser + writer |
427
429
  | [@rip-lang/schema](packages/schema/) | 0.3.8 | Unified schema → TypeScript types, SQL DDL, validation, ORM |
package/bin/rip CHANGED
@@ -173,19 +173,32 @@ async function main() {
173
173
  }
174
174
  }
175
175
 
176
- // Stdin: write to temp .rip file so it flows through the normal execution/compile paths
176
+ // Stdin handling read into memory; only write temp file for the execute path
177
177
  const hasCompileFlag = showCompiled || showTokens || showSExpr || generateDts || generateMap || outputFile;
178
- let isStdin = false;
178
+ let source;
179
+
179
180
  if (!inputFile) {
180
- const tmp = join(process.cwd(), '.__rip_stdin__.rip');
181
181
  try {
182
- writeFileSync(tmp, readFileSync(0, 'utf-8'));
183
- inputFile = tmp;
184
- isStdin = true;
182
+ source = readFileSync(0, 'utf-8');
185
183
  } catch (error) {
186
184
  console.error('Error reading stdin:', error.message);
187
185
  process.exit(1);
188
186
  }
187
+
188
+ if (!hasCompileFlag) {
189
+ const tmp = join(process.cwd(), '.__rip_stdin__.rip');
190
+ try {
191
+ writeFileSync(tmp, source);
192
+ const result = spawnSync('bun', ['--preload', loaderPath, tmp, ...scriptArgs], {
193
+ stdio: 'inherit', env: process.env
194
+ });
195
+ try { unlinkSync(tmp); } catch {}
196
+ process.exit(result.status ?? 1);
197
+ } catch {
198
+ try { unlinkSync(tmp); } catch {}
199
+ process.exit(1);
200
+ }
201
+ }
189
202
  }
190
203
 
191
204
  // Execute script directly (no compile flags)
@@ -195,7 +208,6 @@ async function main() {
195
208
  stdio: 'inherit',
196
209
  env: process.env
197
210
  });
198
- if (isStdin) try { unlinkSync(inputFile); } catch {}
199
211
  process.exit(result.status ?? 1);
200
212
  }
201
213
 
@@ -217,7 +229,7 @@ async function main() {
217
229
 
218
230
  // Fallback: look for bin/{command} in git repo root
219
231
  // Allows `rip migrate --status` to find and run {repo}/bin/migrate
220
- if (!isStdin && inputFile && !inputFile.startsWith('-') && !isFile(inputFile)) {
232
+ if (inputFile && !inputFile.startsWith('-') && !isFile(inputFile)) {
221
233
  try {
222
234
  const repoRoot = execSync('git rev-parse --show-toplevel', {
223
235
  encoding: 'utf-8',
@@ -233,13 +245,14 @@ async function main() {
233
245
 
234
246
  // --- Compile (with flags) ---
235
247
 
236
- if (!isFile(inputFile)) {
237
- console.error(`Error: File not found: ${inputFile}`);
238
- process.exit(1);
248
+ if (source === undefined) {
249
+ if (!isFile(inputFile)) {
250
+ console.error(`Error: File not found: ${inputFile}`);
251
+ process.exit(1);
252
+ }
253
+ source = readFileSync(inputFile, 'utf-8');
239
254
  }
240
255
 
241
- let source = readFileSync(inputFile, 'utf-8');
242
-
243
256
  try {
244
257
  const compiler = new Compiler({ showTokens, showSExpr, quiet,
245
258
  types: generateDts ? 'emit' : undefined,
@@ -263,8 +276,6 @@ async function main() {
263
276
  console.error('Compilation Error:', error.message);
264
277
  if (error.stack) console.error(error.stack);
265
278
  process.exit(1);
266
- } finally {
267
- if (isStdin) try { unlinkSync(inputFile); } catch {}
268
279
  }
269
280
  }
270
281
 
package/docs/RIP-LANG.md CHANGED
@@ -360,6 +360,34 @@ arr?[0] # Compiles to arr?.[0]
360
360
  fn?(arg) # Compiles to fn?.(arg)
361
361
  ```
362
362
 
363
+ ### Optional Chain Assignment
364
+
365
+ Rip extends optional chaining to the left side of assignments. If the
366
+ target is null or undefined, the assignment is silently skipped. This
367
+ eliminates the common `x.prop = val if x` guard pattern.
368
+
369
+ ```coffee
370
+ # Simple property
371
+ el?.scrollTop = 0 # if (el != null) el.scrollTop = 0
372
+
373
+ # Deep chain
374
+ el?.style.display = 'none' # if (el != null) el.style.display = 'none'
375
+
376
+ # Inner chain
377
+ obj.inner?.value = 42 # if (obj.inner != null) obj.inner.value = 42
378
+
379
+ # Bracket access
380
+ arr?[0] = 99 # if (arr != null) arr[0] = 99
381
+
382
+ # Compound operators work too
383
+ counter?.value += 1 # if (counter != null) counter.value += 1
384
+ ```
385
+
386
+ JavaScript does not allow optional chaining on the left side of assignments
387
+ (`x?.prop = val` is a SyntaxError). Rip compiles it to a guarded assignment
388
+ automatically. This is particularly useful for DOM element references that
389
+ may not exist yet (before mount, inside conditionals, etc.).
390
+
363
391
  ## Ternary Operator
364
392
 
365
393
  ```coffee
@@ -420,25 +448,25 @@ or `undefined` if it's falsy. Now you see it… now you don't.
420
448
  (idx is active)?! # ((idx === active) ? true : undefined)
421
449
  ```
422
450
 
423
- Designed for `data-*` attributes in headless UI components, where falsy values
451
+ Designed for `$` attributes (data-* sigil) in headless UI components, where falsy values
424
452
  need to *remove* the attribute rather than set it to `"false"`:
425
453
 
426
454
  ```coffee
427
455
  # Before — verbose and repetitive
428
- data-checked: (@checked or undefined),
429
- data-disabled: (@disabled or undefined),
456
+ $checked: (@checked or undefined),
457
+ $disabled: (@disabled or undefined),
430
458
 
431
459
  # After — clean and expressive
432
- data-checked: @checked?!,
433
- data-disabled: @disabled?!,
460
+ $checked: @checked?!,
461
+ $disabled: @disabled?!,
434
462
  ```
435
463
 
436
464
  Works with any expression, not just identifiers:
437
465
 
438
466
  ```coffee
439
- data-highlighted: (idx is highlightedIndex)?!,
440
- data-selected: (opt.value is String(@value))?!,
441
- data-active: (tab is @active)?!,
467
+ $highlighted: (idx is highlightedIndex)?!,
468
+ $selected: (opt.value is String(@value))?!,
469
+ $active: (tab is @active)?!,
442
470
  ```
443
471
 
444
472
  ## Method Assignment (`.=`)
@@ -1269,11 +1297,11 @@ Rip includes optional packages for full-stack development. All are written in Ri
1269
1297
 
1270
1298
  ```bash
1271
1299
  bun add @rip-lang/server # Web framework + production server
1272
- bun add @rip-lang/grid # Reactive data grid
1273
1300
  bun add @rip-lang/db # DuckDB server + client
1274
1301
  bun add @rip-lang/schema # ORM + validation
1275
1302
  bun add @rip-lang/swarm # Parallel job runner
1276
1303
  bun add @rip-lang/csv # CSV parser + writer
1304
+ # Widgets are included in packages/ui/ (not a separate npm package)
1277
1305
  ```
1278
1306
 
1279
1307
  ## @rip-lang/server — Web Framework & Production Server
@@ -1592,7 +1620,7 @@ App = component
1592
1620
  **Hyphenated Attributes:**
1593
1621
 
1594
1622
  ```coffee
1595
- div data-testid: "main", aria-label: "content"
1623
+ div $testid: "main", aria-label: "content"
1596
1624
  ```
1597
1625
 
1598
1626
  **DOM Properties:**