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/README.md +1 -1
- package/bin/rip +162 -10
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-APP.md +109 -17
- package/docs/RIP-LANG.md +4 -5
- package/docs/RIP-TYPES.md +74 -103
- package/docs/demo/README.md +4 -3
- package/docs/dist/rip.js +933 -338
- package/docs/dist/rip.min.js +209 -204
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +7 -7
- package/docs/example/index.json.br +0 -0
- package/docs/extensions/vscode/print/print-1.0.14.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-0.5.15.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/ui/bundle.json +55 -55
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/index.html +1 -1
- package/package.json +9 -4
- package/rip-loader.js +59 -2
- package/src/AGENTS.md +5 -5
- package/src/browser.js +52 -11
- package/src/compiler.js +318 -44
- package/src/components.js +178 -39
- package/src/dts.js +62 -47
- package/src/lexer.js +58 -15
- package/src/schema/schema.js +5 -5
- package/src/typecheck.js +1355 -100
- package/src/types.js +85 -5
- /package/docs/demo/{components → routes}/_layout.rip +0 -0
- /package/docs/demo/{components → routes}/about.rip +0 -0
- /package/docs/demo/{components → routes}/card.rip +0 -0
- /package/docs/demo/{components → routes}/counter.rip +0 -0
- /package/docs/demo/{components → routes}/index.rip +0 -0
- /package/docs/demo/{components → routes}/todos.rip +0 -0
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
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
295
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
318
|
-
cache: Map<string, any> | null | undefined
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Non-Nullable: `T!`
|
|
305
|
+
function greet(name?: string): void { ... }
|
|
322
306
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
307
|
+
type User = {
|
|
308
|
+
id: number;
|
|
309
|
+
name: string;
|
|
310
|
+
email?: string;
|
|
311
|
+
};
|
|
328
312
|
```
|
|
329
313
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
id: NonNullable<ID>
|
|
334
|
-
user: NonNullable<User>
|
|
335
|
-
```
|
|
314
|
+
### Component Props: `@prop?:: T [:= default]`
|
|
336
315
|
|
|
337
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
330
|
+
export declare class Counter {
|
|
331
|
+
constructor(props: {
|
|
332
|
+
value: number;
|
|
333
|
+
step?: number;
|
|
334
|
+
label?: string;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
356
337
|
```
|
|
357
338
|
|
|
358
|
-
|
|
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
|
-
|
|
361
|
-
property itself may be absent), distinct from value optionality:
|
|
343
|
+
### Expressing Value Optionality
|
|
362
344
|
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
```
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
- **`?`
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
-
| `
|
|
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
|
|
1749
|
+
#### Optionality Marker on Names
|
|
1778
1750
|
|
|
1779
|
-
|
|
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
|
-
|
|
1783
|
-
|
|
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
|
-
|
|
1758
|
+
type User =
|
|
1759
|
+
id:: number
|
|
1760
|
+
email?:: string # optional field
|
|
1761
|
+
```
|
|
1792
1762
|
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
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
|
-
|
|
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
|
|
package/docs/demo/README.md
CHANGED
|
@@ -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
|
-
├──
|
|
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
|
|
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 `
|
|
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.
|