rip-lang 3.17.4 → 3.17.5
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/docs/RIP-LANG.md +90 -3
- package/docs/RIP-TYPES.md +33 -0
- package/docs/demo/routes/card.rip +1 -1
- package/docs/dist/rip.js +524 -123
- package/docs/dist/rip.min.js +230 -203
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +1 -1
- package/docs/example/index.json.br +0 -0
- package/docs/extensions/vscode/print/print-1.0.16.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/vscode-rip-0.8.0.vsix +0 -0
- package/docs/ui/bundle.json +40 -40
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/index.css +8 -0
- package/docs/ui/index.html +4 -4
- package/package.json +5 -5
- package/src/AGENTS.md +43 -10
- package/src/compiler.js +38 -4
- package/src/components.js +474 -89
- package/src/dts.js +26 -28
- package/src/generated/dom-events.js +2 -1
- package/src/generated/dom-tags.js +2 -1
- package/src/grammar/grammar.rip +8 -0
- package/src/parser.js +28 -27
- package/src/typecheck.js +159 -35
- package/src/types.js +153 -0
package/src/dts.js
CHANGED
|
@@ -12,7 +12,7 @@ import { emitTsParam, ripToTs } from "./params.js";
|
|
|
12
12
|
// emitTypes(tokens, sexpr, source) — generates .d.ts from annotated
|
|
13
13
|
// tokens and the parsed s-expression tree.
|
|
14
14
|
//
|
|
15
|
-
// INTRINSIC_TYPE_DECLS / INTRINSIC_FN_DECL /
|
|
15
|
+
// INTRINSIC_TYPE_DECLS / INTRINSIC_FN_DECL /
|
|
16
16
|
// SIGNAL_*, COMPUTED_*, EFFECT_* — declaration tables consumed by
|
|
17
17
|
// emitTypes() and by typecheck.js when building the virtual TS
|
|
18
18
|
// file. Browser code never references these.
|
|
@@ -44,30 +44,20 @@ export const INTRINSIC_TYPE_DECLS = [
|
|
|
44
44
|
// out, which would wrongly drop every SVG attribute. So — like JSX/React — we
|
|
45
45
|
// hand-author the attribute surface as string | number and add it for SVG tags.
|
|
46
46
|
"type __RipSvgAttrs = { cx?: string | number; cy?: string | number; r?: string | number; rx?: string | number; ry?: string | number; x?: string | number; y?: string | number; x1?: string | number; y1?: string | number; x2?: string | number; y2?: string | number; dx?: string | number; dy?: string | number; width?: string | number; height?: string | number; d?: string; points?: string; pathLength?: string | number; viewBox?: string; preserveAspectRatio?: string; transform?: string; 'transform-origin'?: string; fill?: string; 'fill-opacity'?: string | number; 'fill-rule'?: string; stroke?: string; 'stroke-width'?: string | number; 'stroke-linecap'?: string; 'stroke-linejoin'?: string; 'stroke-dasharray'?: string | number; 'stroke-dashoffset'?: string | number; 'stroke-opacity'?: string | number; 'stroke-miterlimit'?: string | number; opacity?: string | number; color?: string; 'clip-path'?: string; 'clip-rule'?: string; mask?: string; filter?: string; 'text-anchor'?: string; 'dominant-baseline'?: string; 'alignment-baseline'?: string; 'font-family'?: string; 'font-size'?: string | number; 'font-weight'?: string | number; 'font-style'?: string; 'letter-spacing'?: string | number; 'word-spacing'?: string | number; 'text-decoration'?: string; 'marker-start'?: string; 'marker-mid'?: string; 'marker-end'?: string; offset?: string | number; 'stop-color'?: string; 'stop-opacity'?: string | number; gradientUnits?: string; gradientTransform?: string; spreadMethod?: string; fx?: string | number; fy?: string | number; fr?: string | number; display?: string; visibility?: string; overflow?: string; 'pointer-events'?: string; cursor?: string; 'vector-effect'?: string; 'shape-rendering'?: string; 'color-interpolation-filters'?: string; href?: string; 'xlink:href'?: string };",
|
|
47
|
-
"type __RipProps<K extends __RipTag> = { [P in __RipAttrKeys<__RipElementMap[K]>]?: __RipElementMap[K][P] } & __RipEvents<K> & {
|
|
47
|
+
"type __RipProps<K extends __RipTag> = { [P in __RipAttrKeys<__RipElementMap[K]>]?: __RipElementMap[K][P] } & __RipEvents<K> & { class?: __RipClassValue | __RipClassValue[]; style?: string; [k: `data-${string}`]: any; [k: `aria-${string}`]: any };",
|
|
48
|
+
// Template-ref validator. `ref:` binds a DOM element into a writable state
|
|
49
|
+
// cell. __RipRefCell<V, K> resolves to `unknown` (an identity intersection)
|
|
50
|
+
// only when V — the cell's full value type — both accepts the tag's element
|
|
51
|
+
// type AND admits null; otherwise it resolves to `never`, collapsing the
|
|
52
|
+
// parameter to `never` so the cell argument fails to assign. The cell shape
|
|
53
|
+
// is written inline (`{ value: V; read(): V }`) rather than `Signal<V>` so
|
|
54
|
+
// the decl is self-contained (no dependency on the Signal interface being in
|
|
55
|
+
// scope) and so a computed (`readonly value`) or a readonly plain field is
|
|
56
|
+
// rejected structurally.
|
|
57
|
+
"type __RipRefCell<V, K extends __RipTag> = [__RipElementMap[K]] extends [NonNullable<V>] ? (null extends V ? unknown : never) : never;",
|
|
48
58
|
];
|
|
49
59
|
|
|
50
|
-
export const INTRINSIC_FN_DECL = 'declare function __ripEl<K extends __RipTag>(tag: K, props?: __RipProps<K>): void;\ndeclare function __ripSvgEl<K extends keyof Omit<SVGElementTagNameMap, keyof HTMLElementTagNameMap>>(tag: K, props?: __RipProps<K> & __RipSvgAttrs): void;\ndeclare function __ripRoute<const T extends string>(s: T): T;';
|
|
51
|
-
|
|
52
|
-
export const ARIA_TYPE_DECLS = [
|
|
53
|
-
'type __RipAriaNavHandlers = { next?: () => void; prev?: () => void; first?: () => void; last?: () => void; select?: () => void; dismiss?: () => void; tab?: () => void; char?: () => void; };',
|
|
54
|
-
"declare const ARIA: {",
|
|
55
|
-
" bindPopover(open: boolean, popover: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, source?: (() => Element | null | undefined) | null): void;",
|
|
56
|
-
" bindDialog(open: boolean, dialog: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, dismissable?: boolean): void;",
|
|
57
|
-
" popupDismiss(open: boolean, popup: () => Element | null | undefined, close: () => void, els?: Array<() => Element | null | undefined>, repos?: (() => void) | null): void;",
|
|
58
|
-
" popupGuard(delay?: number): any;",
|
|
59
|
-
" listNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers): void;",
|
|
60
|
-
" rovingNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers, orientation?: 'vertical' | 'horizontal' | 'both'): void;",
|
|
61
|
-
" positionBelow(trigger: Element | null | undefined, popup: Element | null | undefined, gap?: number, setVisible?: boolean): void;",
|
|
62
|
-
" position(trigger: Element | null | undefined, floating: Element | null | undefined, opts?: any): void;",
|
|
63
|
-
" trapFocus(panel: Element | null | undefined): void;",
|
|
64
|
-
" wireAria(panel: Element, id: string): void;",
|
|
65
|
-
" lockScroll(instance: any): void;",
|
|
66
|
-
" unlockScroll(instance: any): void;",
|
|
67
|
-
" hasAnchor: boolean;",
|
|
68
|
-
" [key: string]: any;",
|
|
69
|
-
"};",
|
|
70
|
-
];
|
|
60
|
+
export const INTRINSIC_FN_DECL = 'declare function __ripEl<K extends __RipTag>(tag: K, props?: __RipProps<K>): void;\ndeclare function __ripSvgEl<K extends keyof Omit<SVGElementTagNameMap, keyof HTMLElementTagNameMap>>(tag: K, props?: __RipProps<K> & __RipSvgAttrs): void;\ndeclare function __ripRoute<const T extends string>(s: T): T;\ndeclare function __ripRef<K extends __RipTag, V>(tag: K, cell: { value: V; read(): V } & __RipRefCell<V, K>): void;';
|
|
71
61
|
|
|
72
62
|
export const SIGNAL_INTERFACE = 'interface Signal<T> { value: T; read(): T; lock(): Signal<T>; free(): Signal<T>; kill(): T; }';
|
|
73
63
|
export const SIGNAL_FN = 'declare function __state<T>(value: T | Signal<T>): Signal<T>;';
|
|
@@ -910,9 +900,6 @@ export function emitTypes(tokens, sexpr = null, source = '', schemaBehavior = nu
|
|
|
910
900
|
if (usesRipIntrinsicProps) {
|
|
911
901
|
preamble.push(...INTRINSIC_TYPE_DECLS);
|
|
912
902
|
}
|
|
913
|
-
if (/\bARIA\./.test(source)) {
|
|
914
|
-
preamble.push(...ARIA_TYPE_DECLS);
|
|
915
|
-
}
|
|
916
903
|
if (usesSignal) {
|
|
917
904
|
preamble.push(SIGNAL_INTERFACE);
|
|
918
905
|
if (!explicitlyBound.has('__state')) preamble.push(SIGNAL_FN);
|
|
@@ -1052,9 +1039,16 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars, so
|
|
|
1052
1039
|
optional = isProp ? !!target[2]?.optional : !!target?.optional;
|
|
1053
1040
|
if (!isProp) {
|
|
1054
1041
|
componentVars.add(propName);
|
|
1055
|
-
let wrapper = (mHead === 'computed') ? 'Computed' : 'Signal';
|
|
1056
1042
|
let typeStr = type ? tsType(type) : (inferLiteralType(member[2]) || 'any');
|
|
1057
|
-
|
|
1043
|
+
if (mHead === 'readonly') {
|
|
1044
|
+
// `=!` member: a genuine const plain field — surface as `readonly`,
|
|
1045
|
+
// NOT Signal-wrapped (the runtime emits `this.name = value`, not a
|
|
1046
|
+
// signal). `readonly` makes a consumer reassignment TS2540.
|
|
1047
|
+
bodyMembers.push(` readonly ${propName}: ${typeStr};`);
|
|
1048
|
+
} else {
|
|
1049
|
+
let wrapper = (mHead === 'computed') ? 'Computed' : 'Signal';
|
|
1050
|
+
bodyMembers.push(` ${propName}: ${wrapper}<${typeStr}>;`);
|
|
1051
|
+
}
|
|
1058
1052
|
continue;
|
|
1059
1053
|
}
|
|
1060
1054
|
} else if (mHead === '.') {
|
|
@@ -1118,6 +1112,10 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars, so
|
|
|
1118
1112
|
publicProps.push(` ${propName}${opt}: ${typeStr};`);
|
|
1119
1113
|
if (mHead === 'state') {
|
|
1120
1114
|
publicProps.push(` __bind_${propName}__?: Signal<${typeStr}>;`);
|
|
1115
|
+
} else if (mHead === 'readonly') {
|
|
1116
|
+
// Public `=!` prop: also surface the instance member as `readonly` so a
|
|
1117
|
+
// consumer reading `instance.name` sees its const-ness (TS2540 on write).
|
|
1118
|
+
bodyMembers.push(` readonly ${propName}: ${typeStr};`);
|
|
1121
1119
|
}
|
|
1122
1120
|
}
|
|
1123
1121
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from TypeScript's lib.dom.d.ts
|
|
2
|
-
// Source: /Users/shreeve/Data/Code/rip-lang/node_modules/.bun/typescript@
|
|
2
|
+
// Source: /Users/shreeve/Data/Code/rip-lang/node_modules/.bun/typescript@6.0.3/node_modules/typescript/lib/lib.dom.d.ts
|
|
3
3
|
// Run: bun scripts/gen-dom.js
|
|
4
4
|
|
|
5
5
|
export const DOM_EVENT_NAMES = [
|
|
@@ -19,6 +19,7 @@ export const DOM_EVENT_NAMES = [
|
|
|
19
19
|
'change',
|
|
20
20
|
'click',
|
|
21
21
|
'close',
|
|
22
|
+
'command',
|
|
22
23
|
'compositionend',
|
|
23
24
|
'compositionstart',
|
|
24
25
|
'compositionupdate',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from TypeScript's lib.dom.d.ts
|
|
2
|
-
// Source: /Users/shreeve/Data/Code/rip-lang/node_modules/.bun/typescript@
|
|
2
|
+
// Source: /Users/shreeve/Data/Code/rip-lang/node_modules/.bun/typescript@6.0.3/node_modules/typescript/lib/lib.dom.d.ts
|
|
3
3
|
// Run: bun scripts/gen-dom.js
|
|
4
4
|
|
|
5
5
|
export const HTML_TAG_NAMES = [
|
|
@@ -184,6 +184,7 @@ export const SVG_TAG_NAMES = [
|
|
|
184
184
|
];
|
|
185
185
|
|
|
186
186
|
export const MATHML_TAG_NAMES = [
|
|
187
|
+
'a',
|
|
187
188
|
'annotation',
|
|
188
189
|
'annotation-xml',
|
|
189
190
|
'maction',
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -973,6 +973,13 @@ grammar =
|
|
|
973
973
|
o '- Expression' , '["-", 2]', prec: 'UNARY_MATH'
|
|
974
974
|
o '+ Expression' , '["+", 2]', prec: 'UNARY_MATH'
|
|
975
975
|
|
|
976
|
+
# Postfix `expr as Type` cast. The type rewriter has already collapsed the
|
|
977
|
+
# `as Type` run into a single CAST marker token whose value is the opaque
|
|
978
|
+
# type string — the grammar never parses a type. Left-associative (chains
|
|
979
|
+
# `x as A as B` nest left-to-right) and looser than arithmetic/member/call
|
|
980
|
+
# but tighter than relational/comparison, matching TS `as` precedence.
|
|
981
|
+
o 'Expression CAST' , '["cast", 1, 2]'
|
|
982
|
+
|
|
976
983
|
# Postfix existence check: expr? → (expr != null)
|
|
977
984
|
o 'Value ?' , '["?", 1]'
|
|
978
985
|
|
|
@@ -1046,6 +1053,7 @@ operators = """
|
|
|
1046
1053
|
left MATH
|
|
1047
1054
|
left + -
|
|
1048
1055
|
left SHIFT
|
|
1056
|
+
left CAST
|
|
1049
1057
|
left RELATION
|
|
1050
1058
|
left COMPARE
|
|
1051
1059
|
left &
|