rip-lang 3.16.0 → 3.16.1

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/docs/RIP-TYPES.md CHANGED
@@ -59,10 +59,7 @@ Both compile to identical JavaScript.
59
59
  |-------|---------|---------|
60
60
  | `::` | Type annotation | `count:: number = 0` |
61
61
  | `type` | Type alias | `type ID = number` |
62
- | `?` | Optional value (`T \| undefined`) | `email:: string?` |
63
- | `??` | Nullable value (`T \| null \| undefined`) | `middle:: string??` |
64
- | `!` | Non-nullable (`NonNullable<T>`) | `id:: ID!` |
65
- | `?:` | Optional property | `email?: string` |
62
+ | `?::` | Optional parameter / field / prop | `email?:: string` |
66
63
  | `\|` | Union member | `"a" \| "b" \| "c"` |
67
64
  | `=>` | Function type arrow | `(a: number) => string` |
68
65
  | `<T>` | Generic parameter | `Container<T>` |
@@ -284,106 +281,78 @@ type Dictionary = {
284
281
 
285
282
  ## Optionality Modifiers
286
283
 
287
- Lightweight suffix operators that map directly to TypeScript unions.
284
+ Rip uses a single optionality marker: `?` placed on the **name**
285
+ (parameter, type-alias field, structural field, or component prop)
286
+ **before** the `::` annotation sigil. There are no `T?` / `T??` / `T!`
287
+ type-suffix operators — write `T | undefined`, `T | null | undefined`,
288
+ or `NonNullable<T>` directly when you need them.
288
289
 
289
- ### Optional: `T?`
290
-
291
- Indicates a value may be undefined.
290
+ ### Optional Parameter / Field: `name?:: T`
292
291
 
293
292
  ```coffee
294
- email:: string?
295
- callback:: Function?
296
- ```
297
-
298
- **Emits:**
299
-
300
- ```ts
301
- email: string | undefined
302
- callback: Function | undefined
303
- ```
293
+ def greet(name?:: string):: void
294
+ console.log "hello #{name ?? 'world'}"
304
295
 
305
- ### Nullable Optional: `T??`
306
-
307
- Indicates a value may be null or undefined.
308
-
309
- ```coffee
310
- middle:: string??
311
- cache:: Map<string, any>??
296
+ type User =
297
+ id:: number
298
+ name:: string
299
+ email?:: string # Optional field — may be absent
312
300
  ```
313
301
 
314
302
  **Emits:**
315
303
 
316
304
  ```ts
317
- middle: string | null | undefined
318
- cache: Map<string, any> | null | undefined
319
- ```
320
-
321
- ### Non-Nullable: `T!`
305
+ function greet(name?: string): void { ... }
322
306
 
323
- Asserts a value is never null or undefined.
324
-
325
- ```coffee
326
- id:: ID!
327
- user:: User!
307
+ type User = {
308
+ id: number;
309
+ name: string;
310
+ email?: string;
311
+ };
328
312
  ```
329
313
 
330
- **Emits:**
331
-
332
- ```ts
333
- id: NonNullable<ID>
334
- user: NonNullable<User>
335
- ```
314
+ ### Component Props: `@prop?:: T [:= default]`
336
315
 
337
- ### In Function Signatures
316
+ A `?` on a component prop name marks it optional in the generated
317
+ constructor type. A `:=` default is purely a runtime initializer — it
318
+ does NOT make the prop optional on its own. The three canonical shapes:
338
319
 
339
320
  ```coffee
340
- def findUser(id:: number):: User?
341
- db.find(id) or undefined
342
-
343
- def getUser(id:: number):: User!
344
- db.find(id) ?? throw new Error "Not found"
345
-
346
- def updateUser(id:: number, email:: string??):: boolean
347
- ...
321
+ export Counter = component
322
+ @value:: number # required, no default
323
+ @step?:: number # optional, no default
324
+ @label?:: string := "Count" # optional with default
348
325
  ```
349
326
 
350
327
  **Emits:**
351
328
 
352
329
  ```ts
353
- function findUser(id: number): User | undefined { ... }
354
- function getUser(id: number): NonNullable<User> { ... }
355
- function updateUser(id: number, email: string | null | undefined): boolean { ... }
330
+ export declare class Counter {
331
+ constructor(props: {
332
+ value: number;
333
+ step?: number;
334
+ label?: string;
335
+ });
336
+ }
356
337
  ```
357
338
 
358
- ### Optional Properties
339
+ Writing `@prop:: T := V` (typed, defaulted, no `?`) declares a
340
+ **required prop with a default** — the parent must still pass it. To
341
+ make a prop optional, add `?` to the name.
359
342
 
360
- In structural types, `?` after the property name makes it optional (the
361
- property itself may be absent), distinct from value optionality:
343
+ ### Expressing Value Optionality
362
344
 
363
- ```coffee
364
- type User =
365
- id: number
366
- name: string
367
- email?: string # Optional property — may be absent
368
- phone?: string? # Optional property that can also be undefined when present
369
- ```
370
-
371
- **Emits:**
345
+ When the **value** itself needs to permit `undefined` or `null`, write
346
+ the union explicitly:
372
347
 
373
- ```ts
374
- type User = {
375
- id: number;
376
- name: string;
377
- email?: string;
378
- phone?: string | undefined;
379
- };
348
+ ```coffee
349
+ email:: string | undefined
350
+ middle:: string | null | undefined
351
+ id:: NonNullable<ID>
380
352
  ```
381
353
 
382
- ### Key Distinction
383
-
384
- - `email?: string` — property may be absent
385
- - `email:: string?` — value may be undefined
386
- - `email?: string??` — property may be absent, value may be null or undefined
354
+ This is the same form TypeScript uses, so the emitted DTS is a
355
+ character-for-character match.
387
356
 
388
357
  ---
389
358
 
@@ -647,18 +616,20 @@ Types are optional and gradual:
647
616
 
648
617
  ### Project Level
649
618
 
650
- Configure via `rip.json` or the `"rip"` key in `package.json`:
619
+ Configure via the `"rip"` key in `package.json`:
651
620
 
652
621
  ```json
653
622
  {
654
623
  "strict": true,
624
+ "checkAll": true,
655
625
  "exclude": ["vendor/**", "legacy/**"]
656
626
  }
657
627
  ```
658
628
 
659
629
  | Key | Purpose |
660
630
  |-----|--------|
661
- | `strict` | Enable strict mode for `rip check` (default: `false`) |
631
+ | `strict` | Enable TS strict family for `rip check` (default: `false`) |
632
+ | `checkAll` | Type-check every non-`@nocheck` `.rip` file, not just annotated ones (default: `false`) |
662
633
  | `exclude` | Glob patterns for files to skip during `rip check` |
663
634
 
664
635
  ### File Level
@@ -1153,9 +1124,10 @@ collection — the token itself is the answer:
1153
1124
 
1154
1125
  **Other special cases:**
1155
1126
 
1156
- - **`?` / `??` / `!` suffixes**: These modify the type. When they appear
1157
- unspaced after an identifier at depth 0, they are part of the type:
1158
- `string?` `"string?"`, `ID!` `"ID!"`.
1127
+ - **`?` on names**: When `?` appears unspaced **before** a `::` annotation
1128
+ on a parameter, field, or component prop name, the lexer marks the name
1129
+ token as optional (`data.optional = true`). This is the sole optionality
1130
+ marker — there are no `?` / `??` / `!` suffixes on type expressions.
1159
1131
  - **Generic `<>` vs comparison**: Inside a type context (after `::`), always
1160
1132
  treat `<` as opening a generic bracket. The type context is unambiguous
1161
1133
  because we are already inside a `::` collection.
@@ -1341,12 +1313,12 @@ syntax into standard TypeScript:
1341
1313
  | Rip syntax | TypeScript equivalent |
1342
1314
  |-----------|---------------------|
1343
1315
  | `::` | `:` (annotation sigil to type separator) |
1344
- | `T?` | `T \| undefined` |
1345
- | `T??` | `T \| null \| undefined` |
1346
- | `T!` | `NonNullable<T>` |
1316
+ | `name?::` | `name?:` (optional parameter / field / prop) |
1347
1317
 
1348
1318
  Function type expressions use `=>` directly (same as TypeScript), so no
1349
- arrow conversion is needed.
1319
+ arrow conversion is needed. Value-level optionality is written with
1320
+ explicit unions (`T | undefined`, `T | null`, `NonNullable<T>`) — there
1321
+ are no type-suffix operators.
1350
1322
 
1351
1323
  The function returns a `.d.ts` string. Declarations without type
1352
1324
  annotations are skipped — only annotated code appears in the output.
@@ -1774,29 +1746,28 @@ Type annotations never affect `let` vs `const` in `.js` output:
1774
1746
 
1775
1747
  ### Edge Cases
1776
1748
 
1777
- #### Optionality Suffixes in Types
1749
+ #### Optionality Marker on Names
1778
1750
 
1779
- The `?`, `??`, and `!` suffixes appear unspaced after the type name:
1751
+ A `?` placed unspaced **before** the `::` annotation on a parameter,
1752
+ field, or component prop name marks the name as optional:
1780
1753
 
1781
1754
  ```coffee
1782
- email:: string? # type = "string?"
1783
- middle:: string?? # → type = "string??"
1784
- id:: ID! # → type = "ID!"
1785
- ```
1786
-
1787
- In `rewriteTypes()`, when collecting type tokens, if the next token is `?`,
1788
- `??`, or `!` and is **not spaced** from the previous token, include it as
1789
- part of the type string.
1755
+ def fetch(url:: string, opts?:: RequestInit):: Response
1756
+ ...
1790
1757
 
1791
- The `emitTypes()` function expands these suffixes into standard TypeScript:
1758
+ type User =
1759
+ id:: number
1760
+ email?:: string # optional field
1761
+ ```
1792
1762
 
1793
- | Rip suffix | TypeScript equivalent |
1794
- |-----------|---------------------|
1795
- | `T?` | `T \| undefined` |
1796
- | `T??` | `T \| null \| undefined` |
1797
- | `T!` | `NonNullable<T>` |
1763
+ In the lexer, when `?` appears immediately before `::`, it is stripped
1764
+ from the stream and `data.optional = true` is set on the preceding
1765
+ name token. `emitTypes()` reads that flag and emits `name?:` instead of
1766
+ `name:` in the generated TypeScript.
1798
1767
 
1799
- See §1.6 for the full Rip-to-TypeScript conversion table (including `::` → `:`).
1768
+ Value-level optionality is expressed with explicit unions there are
1769
+ no `T?` / `T??` / `T!` type-suffix operators. See §1.6 for the full
1770
+ Rip-to-TypeScript conversion table.
1800
1771
 
1801
1772
  #### File-Level Type Directives
1802
1773
 
@@ -6,7 +6,7 @@ The canonical "everything in one file" demo of the Rip App framework. Six compon
6
6
 
7
7
  ```
8
8
  docs/demo/
9
- ├── components/
9
+ ├── routes/
10
10
  │ ├── _layout.rip — root layout with nav + error boundary
11
11
  │ ├── index.rip — Home page (file-based routing: / → index.rip)
12
12
  │ ├── counter.rip — reactive state, := / ~> persistence to stash
@@ -22,7 +22,8 @@ docs/demo/
22
22
  ```bash
23
23
  bun run bundle:demo
24
24
  # wraps:
25
- # bun scripts/bundle-app.js docs/demo -o docs/example/index.json -t "Rip App Demo"
25
+ # bun scripts/bundle-app.js docs/demo/routes --prefix _route --css docs/demo/css \
26
+ # -o docs/example/index.json -t "Rip App Demo"
26
27
  ```
27
28
 
28
29
  Output: `docs/example/index.json` — a single ~17 KB file containing every component's raw `.rip` source plus all CSS, ready to ship to any static host. The launcher at `docs/example/index.html` fetches it once and runs the whole app from memory: no bundler, no build step, no per-component network requests.
@@ -38,6 +39,6 @@ Then `<script type="text/rip">launch bundle: bundle</script>` mounts everything.
38
39
 
39
40
  ## Iterating
40
41
 
41
- Edit any `.rip` file under `components/`, then re-run `bun run bundle:demo` to refresh the bundled JSON. The launcher HTML at `docs/example/` will pick up the new bundle on next load.
42
+ Edit any `.rip` file under `routes/`, then re-run `bun run bundle:demo` to refresh the bundled JSON. The launcher HTML at `docs/example/` will pick up the new bundle on next load.
42
43
 
43
44
  CSS is concatenated alphabetically by filename. Add `.css` files under `css/` to extend the design.