rip-lang 3.13.134 → 3.13.136
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/CHANGELOG.md +0 -1
- package/README.md +4 -7
- package/bin/rip +16 -4
- package/docs/RIP-LANG.md +0 -42
- package/docs/RIP-TYPES.md +47 -52
- package/docs/demo.html +2 -2
- package/docs/dist/rip.js +1039 -449
- package/docs/dist/rip.min.js +168 -164
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/rip-loader.js +2 -2
- package/src/AGENTS.md +1 -2
- package/src/browser.js +5 -5
- package/src/compiler.js +106 -28
- package/src/components.js +176 -11
- package/src/error.js +250 -0
- package/src/grammar/grammar.rip +2 -12
- package/src/lexer.js +15 -11
- package/src/parser.js +220 -223
- package/src/repl.js +3 -2
- package/src/sourcemap-utils.js +39 -6
- package/src/typecheck.js +365 -80
- package/src/types.js +226 -51
- package/src/ui.rip +4 -0
package/src/types.js
CHANGED
|
@@ -13,6 +13,52 @@
|
|
|
13
13
|
// emitEnum() — the one CodeEmitter method for runtime enum output.
|
|
14
14
|
// Enums cross into the grammar because they emit runtime JavaScript.
|
|
15
15
|
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Shared type declaration constants — single source of truth
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Used by emitTypes() for .d.ts emission and by compileForCheck() in
|
|
20
|
+
// typecheck.js for virtual .ts injection. Keeping them here eliminates
|
|
21
|
+
// divergence between what gets written to disk and what TS analyzes.
|
|
22
|
+
|
|
23
|
+
export const INTRINSIC_TYPE_DECLS = [
|
|
24
|
+
'type __RipElementMap = HTMLElementTagNameMap & Omit<SVGElementTagNameMap, keyof HTMLElementTagNameMap>;',
|
|
25
|
+
'type __RipTag = keyof __RipElementMap;',
|
|
26
|
+
"type __RipBrowserElement = Omit<HTMLElement, 'querySelector' | 'querySelectorAll' | 'closest' | 'setAttribute' | 'hidden'> & { hidden: boolean | 'until-found'; setAttribute(qualifiedName: string, value: any): void; querySelector(selectors: string): __RipBrowserElement | null; querySelectorAll(selectors: string): NodeListOf<__RipBrowserElement>; closest(selectors: string): __RipBrowserElement | null; };",
|
|
27
|
+
"type __RipDomEl<K extends __RipTag> = Omit<__RipElementMap[K], 'querySelector' | 'querySelectorAll' | 'closest' | 'setAttribute' | 'hidden'> & __RipBrowserElement;",
|
|
28
|
+
"type __RipAttrKeys<T> = { [K in keyof T]-?: K extends 'style' | 'classList' | 'className' | 'nodeValue' | 'textContent' | 'innerHTML' | 'innerText' | 'outerHTML' | 'outerText' | 'scrollLeft' | 'scrollTop' ? never : K extends `on${string}` | `aria${string}Element` | `aria${string}Elements` ? never : T[K] extends (...args: any[]) => any ? never : (<V>() => V extends Pick<T, K> ? 1 : 2) extends (<V>() => V extends { -readonly [P in K]: T[P] } ? 1 : 2) ? K : never }[keyof T] & string;",
|
|
29
|
+
'type __RipEvents = { [K in keyof HTMLElementEventMap as `@${K}`]?: ((event: HTMLElementEventMap[K]) => void) | null };',
|
|
30
|
+
'type __RipClassValue = string | boolean | null | undefined | Record<string, boolean> | __RipClassValue[];',
|
|
31
|
+
'type __RipProps<K extends __RipTag> = { [P in __RipAttrKeys<__RipElementMap[K]>]?: __RipElementMap[K][P] } & __RipEvents & { ref?: string; class?: __RipClassValue | __RipClassValue[]; style?: string; [k: `data-${string}`]: any; [k: `aria-${string}`]: any };',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export const INTRINSIC_FN_DECL = 'declare function __ripEl<K extends __RipTag>(tag: K, props?: __RipProps<K>): void;';
|
|
35
|
+
|
|
36
|
+
export const ARIA_TYPE_DECLS = [
|
|
37
|
+
'type __RipAriaNavHandlers = { next?: () => void; prev?: () => void; first?: () => void; last?: () => void; select?: () => void; dismiss?: () => void; tab?: () => void; char?: () => void; };',
|
|
38
|
+
"declare const ARIA: {",
|
|
39
|
+
" bindPopover(open: boolean, popover: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, source?: (() => Element | null | undefined) | null): void;",
|
|
40
|
+
" bindDialog(open: boolean, dialog: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, dismissable?: boolean): void;",
|
|
41
|
+
" popupDismiss(open: boolean, popup: () => Element | null | undefined, close: () => void, els?: Array<() => Element | null | undefined>, repos?: (() => void) | null): void;",
|
|
42
|
+
" popupGuard(delay?: number): any;",
|
|
43
|
+
" listNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers): void;",
|
|
44
|
+
" rovingNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers, orientation?: 'vertical' | 'horizontal' | 'both'): void;",
|
|
45
|
+
" positionBelow(trigger: Element | null | undefined, popup: Element | null | undefined, gap?: number, setVisible?: boolean): void;",
|
|
46
|
+
" position(trigger: Element | null | undefined, floating: Element | null | undefined, opts?: any): void;",
|
|
47
|
+
" trapFocus(panel: Element | null | undefined): void;",
|
|
48
|
+
" wireAria(panel: Element, id: string): void;",
|
|
49
|
+
" lockScroll(instance: any): void;",
|
|
50
|
+
" unlockScroll(instance: any): void;",
|
|
51
|
+
" hasAnchor: boolean;",
|
|
52
|
+
" [key: string]: any;",
|
|
53
|
+
"};",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export const SIGNAL_INTERFACE = 'interface Signal<T> { value: T; read(): T; lock(): Signal<T>; free(): Signal<T>; kill(): T; }';
|
|
57
|
+
export const SIGNAL_FN = 'declare function __state<T>(value: T | Signal<T>): Signal<T>;';
|
|
58
|
+
export const COMPUTED_INTERFACE = 'interface Computed<T> { readonly value: T; read(): T; lock(): Computed<T>; free(): Computed<T>; kill(): T; }';
|
|
59
|
+
export const COMPUTED_FN = 'declare function __computed<T>(fn: () => T): Computed<T>;';
|
|
60
|
+
export const EFFECT_FN = 'declare function __effect(fn: () => void | (() => void)): () => void;';
|
|
61
|
+
|
|
16
62
|
// ============================================================================
|
|
17
63
|
// installTypeSupport — adds rewriteTypes() to Lexer.prototype
|
|
18
64
|
// ============================================================================
|
|
@@ -35,6 +81,7 @@ export function installTypeSupport(Lexer) {
|
|
|
35
81
|
|
|
36
82
|
proto.rewriteTypes = function() {
|
|
37
83
|
let tokens = this.tokens;
|
|
84
|
+
let typeRefNames = this.typeRefNames = new Set();
|
|
38
85
|
let gen = (tag, val, origin) => {
|
|
39
86
|
let t = [tag, val];
|
|
40
87
|
t.pre = 0;
|
|
@@ -50,27 +97,33 @@ export function installTypeSupport(Lexer) {
|
|
|
50
97
|
this.scanTokens((token, i, tokens) => {
|
|
51
98
|
let tag = token[0];
|
|
52
99
|
|
|
53
|
-
// ── Generic type parameters: DEF name<T>(...)
|
|
100
|
+
// ── Generic type parameters: DEF name<T>(...) or Name<T> = component ──
|
|
54
101
|
// (Generic params on type aliases are handled by the `type` keyword handler below)
|
|
55
102
|
if (tag === 'IDENTIFIER') {
|
|
56
103
|
let next = tokens[i + 1];
|
|
57
104
|
if (next && next[0] === 'COMPARE' && next[1] === '<' && !next.spaced) {
|
|
58
105
|
let isDef = tokens[i - 1]?.[0] === 'DEF';
|
|
59
106
|
let genTokens = collectBalancedAngles(tokens, i + 1);
|
|
60
|
-
if (genTokens
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
tokens
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
m
|
|
107
|
+
if (genTokens) {
|
|
108
|
+
// Check for component pattern: Name<T> = component
|
|
109
|
+
let afterAngles = i + 1 + genTokens.length;
|
|
110
|
+
let isComponent = !isDef && tokens[afterAngles]?.[0] === '=' &&
|
|
111
|
+
tokens[afterAngles + 1]?.[0] === 'COMPONENT';
|
|
112
|
+
if (isDef || isComponent) {
|
|
113
|
+
if (!token.data) token.data = {};
|
|
114
|
+
token.data.typeParams = buildTypeString(genTokens);
|
|
115
|
+
tokens.splice(i + 1, genTokens.length);
|
|
116
|
+
// After removing <T>, retag ( as CALL_START if it follows DEF IDENTIFIER
|
|
117
|
+
if (isDef && tokens[i + 1]?.[0] === '(') {
|
|
118
|
+
tokens[i + 1][0] = 'CALL_START';
|
|
119
|
+
// Find matching ) and retag as CALL_END
|
|
120
|
+
let d = 1, m = i + 2;
|
|
121
|
+
while (m < tokens.length && d > 0) {
|
|
122
|
+
if (tokens[m][0] === '(' || tokens[m][0] === 'CALL_START') d++;
|
|
123
|
+
if (tokens[m][0] === ')' || tokens[m][0] === 'CALL_END') d--;
|
|
124
|
+
if (d === 0) tokens[m][0] = 'CALL_END';
|
|
125
|
+
m++;
|
|
126
|
+
}
|
|
74
127
|
}
|
|
75
128
|
}
|
|
76
129
|
}
|
|
@@ -102,7 +155,7 @@ export function installTypeSupport(Lexer) {
|
|
|
102
155
|
propName = 'returnType';
|
|
103
156
|
} else if (prevToken[0] === 'PARAM_END') {
|
|
104
157
|
// Return type on arrow function — scan forward to -> token
|
|
105
|
-
let arrowIdx = i + 1 + typeTokens.
|
|
158
|
+
let arrowIdx = i + 1 + typeTokens.consumed;
|
|
106
159
|
let arrowToken = tokens[arrowIdx];
|
|
107
160
|
if (arrowToken && (arrowToken[0] === '->' || arrowToken[0] === '=>')) {
|
|
108
161
|
target = arrowToken;
|
|
@@ -117,8 +170,13 @@ export function installTypeSupport(Lexer) {
|
|
|
117
170
|
if (!target.data) target.data = {};
|
|
118
171
|
target.data[propName] = typeStr;
|
|
119
172
|
|
|
173
|
+
// Track identifiers used in type annotations for import elision
|
|
174
|
+
for (let tt of typeTokens) {
|
|
175
|
+
if (tt[0] === 'IDENTIFIER') typeRefNames.add(tt[1]);
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
// Remove :: and type tokens from stream
|
|
121
|
-
let removeCount = 1 + typeTokens.
|
|
179
|
+
let removeCount = 1 + typeTokens.consumed;
|
|
122
180
|
tokens.splice(i, removeCount);
|
|
123
181
|
return 0;
|
|
124
182
|
}
|
|
@@ -180,7 +238,7 @@ export function installTypeSupport(Lexer) {
|
|
|
180
238
|
|
|
181
239
|
// Simple alias: type Name = type-expression
|
|
182
240
|
let typeTokens = collectTypeExpression(tokens, afterEq);
|
|
183
|
-
tokens.splice(removeFrom, afterEq + typeTokens.
|
|
241
|
+
tokens.splice(removeFrom, afterEq + typeTokens.consumed - removeFrom, makeDecl(buildTypeString(typeTokens)));
|
|
184
242
|
return 0;
|
|
185
243
|
}
|
|
186
244
|
|
|
@@ -225,6 +283,61 @@ export function installTypeSupport(Lexer) {
|
|
|
225
283
|
|
|
226
284
|
return 1;
|
|
227
285
|
});
|
|
286
|
+
|
|
287
|
+
// ── Second pass: detect bodiless typed DEF (overload signatures) ──────
|
|
288
|
+
// Pattern: DEF IDENTIFIER CALL_START ... CALL_END TERMINATOR (no INDENT body)
|
|
289
|
+
// These are type-only overload declarations — remove from token stream
|
|
290
|
+
// and emit as TYPE_DECL markers so emitTypes() can generate DTS lines.
|
|
291
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
292
|
+
if (tokens[i][0] !== 'DEF') continue;
|
|
293
|
+
let nameToken = tokens[i + 1];
|
|
294
|
+
if (!nameToken || nameToken[0] !== 'IDENTIFIER') continue;
|
|
295
|
+
|
|
296
|
+
// Find CALL_END
|
|
297
|
+
let j = i + 2;
|
|
298
|
+
if (tokens[j]?.[0] !== 'CALL_START') continue;
|
|
299
|
+
let depth = 1;
|
|
300
|
+
j++;
|
|
301
|
+
while (j < tokens.length && depth > 0) {
|
|
302
|
+
if (tokens[j][0] === 'CALL_START') depth++;
|
|
303
|
+
if (tokens[j][0] === 'CALL_END') depth--;
|
|
304
|
+
j++;
|
|
305
|
+
}
|
|
306
|
+
// j is now past CALL_END
|
|
307
|
+
let callEndIdx = j - 1;
|
|
308
|
+
|
|
309
|
+
// Bodiless = next is TERMINATOR or EOF (not INDENT)
|
|
310
|
+
let next = tokens[j];
|
|
311
|
+
if (next && next[0] !== 'TERMINATOR') continue;
|
|
312
|
+
|
|
313
|
+
// Must have type annotations to qualify as an overload signature
|
|
314
|
+
let hasTypes = nameToken.data?.returnType;
|
|
315
|
+
if (!hasTypes) {
|
|
316
|
+
for (let k = i + 2; k <= callEndIdx; k++) {
|
|
317
|
+
if (tokens[k].data?.type) { hasTypes = true; break; }
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (!hasTypes) continue;
|
|
321
|
+
|
|
322
|
+
// Save the overload tokens for emitTypes' collectParams
|
|
323
|
+
let overloadTokens = tokens.slice(i, j + 1); // DEF through TERMINATOR
|
|
324
|
+
|
|
325
|
+
// Check for export before DEF
|
|
326
|
+
let exported = i >= 1 && tokens[i - 1]?.[0] === 'EXPORT';
|
|
327
|
+
let spliceFrom = exported ? i - 1 : i;
|
|
328
|
+
let spliceCount = (j + 1) - spliceFrom; // include TERMINATOR
|
|
329
|
+
|
|
330
|
+
let marker = gen('TYPE_DECL', nameToken[1], nameToken);
|
|
331
|
+
marker.data = {
|
|
332
|
+
name: nameToken[1],
|
|
333
|
+
kind: 'overload',
|
|
334
|
+
overloadTokens,
|
|
335
|
+
exported,
|
|
336
|
+
};
|
|
337
|
+
if (nameToken.data?.typeParams) marker.data.typeParams = nameToken.data.typeParams;
|
|
338
|
+
|
|
339
|
+
tokens.splice(spliceFrom, spliceCount, marker);
|
|
340
|
+
}
|
|
228
341
|
};
|
|
229
342
|
}
|
|
230
343
|
|
|
@@ -236,6 +349,7 @@ export function installTypeSupport(Lexer) {
|
|
|
236
349
|
function collectTypeExpression(tokens, j) {
|
|
237
350
|
let typeTokens = [];
|
|
238
351
|
let depth = 0;
|
|
352
|
+
let startJ = j;
|
|
239
353
|
|
|
240
354
|
while (j < tokens.length) {
|
|
241
355
|
let t = tokens[j];
|
|
@@ -275,6 +389,17 @@ function collectTypeExpression(tokens, j) {
|
|
|
275
389
|
|
|
276
390
|
// Delimiters that end the type at depth 0
|
|
277
391
|
if (depth === 0) {
|
|
392
|
+
// After =>, INDENT wraps the return type body — collect through OUTDENT
|
|
393
|
+
if (tTag === 'INDENT' && typeTokens.length > 0 && typeTokens[typeTokens.length - 1][0] === '=>') {
|
|
394
|
+
j++; // skip INDENT
|
|
395
|
+
let nest = 1;
|
|
396
|
+
while (j < tokens.length && nest > 0) {
|
|
397
|
+
if (tokens[j][0] === 'INDENT') { nest++; j++; }
|
|
398
|
+
else if (tokens[j][0] === 'OUTDENT') { nest--; j++; }
|
|
399
|
+
else { typeTokens.push(tokens[j]); j++; }
|
|
400
|
+
}
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
278
403
|
if (tTag === '=' || tTag === 'REACTIVE_ASSIGN' ||
|
|
279
404
|
tTag === 'COMPUTED_ASSIGN' || tTag === 'READONLY_ASSIGN' ||
|
|
280
405
|
tTag === 'EFFECT' || tTag === 'TERMINATOR' ||
|
|
@@ -290,6 +415,8 @@ function collectTypeExpression(tokens, j) {
|
|
|
290
415
|
j++;
|
|
291
416
|
}
|
|
292
417
|
|
|
418
|
+
typeTokens.consumed = j - startJ;
|
|
419
|
+
|
|
293
420
|
return typeTokens;
|
|
294
421
|
}
|
|
295
422
|
|
|
@@ -306,7 +433,8 @@ function buildTypeString(typeTokens) {
|
|
|
306
433
|
.replace(/\s*,\s*/g, ', ')
|
|
307
434
|
.replace(/\s*=>\s*/g, ' => ')
|
|
308
435
|
.replace(/ :: /g, ': ')
|
|
309
|
-
.replace(/:: /g, ': ')
|
|
436
|
+
.replace(/:: /g, ': ')
|
|
437
|
+
.replace(/ : /g, ': ');
|
|
310
438
|
return typeStr;
|
|
311
439
|
}
|
|
312
440
|
|
|
@@ -348,6 +476,32 @@ function collectStructuralType(tokens, indentIdx) {
|
|
|
348
476
|
}
|
|
349
477
|
if (t[0] === 'TERMINATOR') { j++; continue; }
|
|
350
478
|
|
|
479
|
+
// Index signature: [key: Type]: ValueType
|
|
480
|
+
if (depth === 1 && t[0] === '[') {
|
|
481
|
+
let sigTokens = [];
|
|
482
|
+
j++; // skip [
|
|
483
|
+
// Collect tokens through matching ]
|
|
484
|
+
while (j < tokens.length && tokens[j][0] !== ']') {
|
|
485
|
+
sigTokens.push(tokens[j]);
|
|
486
|
+
j++;
|
|
487
|
+
}
|
|
488
|
+
j++; // skip ]
|
|
489
|
+
// Skip : separator after ]
|
|
490
|
+
if (tokens[j]?.[1] === ':' || tokens[j]?.[0] === 'TYPE_ANNOTATION') j++;
|
|
491
|
+
// Collect value type
|
|
492
|
+
let valTypeTokens = [];
|
|
493
|
+
while (j < tokens.length) {
|
|
494
|
+
let pt = tokens[j];
|
|
495
|
+
if (pt[0] === 'TERMINATOR' || pt[0] === 'OUTDENT') break;
|
|
496
|
+
valTypeTokens.push(pt);
|
|
497
|
+
j++;
|
|
498
|
+
}
|
|
499
|
+
let sigStr = buildTypeString(sigTokens);
|
|
500
|
+
let valStr = buildTypeString(valTypeTokens);
|
|
501
|
+
props.push(`[${sigStr}]: ${valStr}`);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
|
|
351
505
|
// Collect a property line: name (? optional) : type
|
|
352
506
|
// Property tokens can be PROPERTY, IDENTIFIER, or keyword tags whose
|
|
353
507
|
// value is a valid identifier (e.g. RENDER "render" in interfaces).
|
|
@@ -385,6 +539,20 @@ function collectStructuralType(tokens, indentIdx) {
|
|
|
385
539
|
let typeDepth = 0;
|
|
386
540
|
while (j < tokens.length) {
|
|
387
541
|
let pt = tokens[j];
|
|
542
|
+
// Nested structural type: `prop: type` followed by INDENT block
|
|
543
|
+
if (pt[0] === 'IDENTIFIER' && pt[1] === 'type' && tokens[j + 1]?.[0] === 'INDENT') {
|
|
544
|
+
j++; // skip 'type'
|
|
545
|
+
let nestedType = collectStructuralType(tokens, j);
|
|
546
|
+
propTypeTokens.push(['', nestedType]);
|
|
547
|
+
// Skip past the INDENT...OUTDENT block
|
|
548
|
+
let nd = 1; j++;
|
|
549
|
+
while (j < tokens.length && nd > 0) {
|
|
550
|
+
if (tokens[j][0] === 'INDENT') nd++;
|
|
551
|
+
if (tokens[j][0] === 'OUTDENT') nd--;
|
|
552
|
+
j++;
|
|
553
|
+
}
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
388
556
|
if (pt[0] === 'INDENT') { typeDepth++; j++; continue; }
|
|
389
557
|
if (pt[0] === 'OUTDENT') {
|
|
390
558
|
if (typeDepth > 0) { typeDepth--; j++; continue; }
|
|
@@ -514,7 +682,19 @@ export function emitTypes(tokens, sexpr = null, source = '') {
|
|
|
514
682
|
else if (body[c] === '}') { depth--; if (depth === 0) { firstTopClose = c; break; } }
|
|
515
683
|
}
|
|
516
684
|
if (firstTopClose === body.length - 1) {
|
|
517
|
-
|
|
685
|
+
// Depth-aware split on '; ' at top level only
|
|
686
|
+
let inner = body.slice(2, -2);
|
|
687
|
+
let props = [], start = 0, d = 0;
|
|
688
|
+
for (let c = 0; c < inner.length; c++) {
|
|
689
|
+
if (inner[c] === '{') d++;
|
|
690
|
+
else if (inner[c] === '}') d--;
|
|
691
|
+
else if (d === 0 && inner[c] === ';' && inner[c + 1] === ' ') {
|
|
692
|
+
props.push(inner.slice(start, c));
|
|
693
|
+
start = c + 2;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (start < inner.length) props.push(inner.slice(start));
|
|
697
|
+
props = props.filter(p => p.trim());
|
|
518
698
|
if (props.length > 0) {
|
|
519
699
|
lines.push(`${indent()}${prefix}{`);
|
|
520
700
|
indentLevel++;
|
|
@@ -839,7 +1019,21 @@ export function emitTypes(tokens, sexpr = null, source = '') {
|
|
|
839
1019
|
let exp = (exported || data.exported) ? 'export ' : '';
|
|
840
1020
|
let params = data.typeParams || '';
|
|
841
1021
|
|
|
842
|
-
if (data.kind === '
|
|
1022
|
+
if (data.kind === 'overload') {
|
|
1023
|
+
// Emit function overload signature from saved tokens
|
|
1024
|
+
let ot = data.overloadTokens;
|
|
1025
|
+
let nameToken = ot[1]; // DEF is [0], name is [1]
|
|
1026
|
+
let { params: paramList } = collectParams(ot, 2);
|
|
1027
|
+
let returnType = nameToken.data?.returnType;
|
|
1028
|
+
let ret = returnType ? `: ${expandSuffixes(returnType)}` : '';
|
|
1029
|
+
let declare = inClass ? '' : (exp ? '' : 'declare ');
|
|
1030
|
+
let typeParams = data.typeParams || '';
|
|
1031
|
+
if (inClass) {
|
|
1032
|
+
lines.push(`${indent()}${data.name}${typeParams}(${paramList.join(', ')})${ret};`);
|
|
1033
|
+
} else {
|
|
1034
|
+
lines.push(`${indent()}${exp}${declare}function ${data.name}${typeParams}(${paramList.join(', ')})${ret};`);
|
|
1035
|
+
}
|
|
1036
|
+
} else if (data.kind === 'interface') {
|
|
843
1037
|
let ext = data.extends ? ` extends ${data.extends}` : '';
|
|
844
1038
|
emitBlock(`${exp}interface ${data.name}${params}${ext} `, data.typeText || '{}', '');
|
|
845
1039
|
} else {
|
|
@@ -1143,43 +1337,21 @@ export function emitTypes(tokens, sexpr = null, source = '') {
|
|
|
1143
1337
|
// Prepend reactive type definitions if used
|
|
1144
1338
|
let preamble = [];
|
|
1145
1339
|
if (usesRipIntrinsicProps) {
|
|
1146
|
-
preamble.push(
|
|
1147
|
-
preamble.push('type __RipTag = keyof __RipElementMap;');
|
|
1148
|
-
preamble.push("type __RipBrowserElement = Omit<HTMLElement, 'querySelector' | 'querySelectorAll' | 'closest' | 'setAttribute' | 'hidden'> & { hidden: boolean | 'until-found'; setAttribute(qualifiedName: string, value: any): void; querySelector(selectors: string): __RipBrowserElement | null; querySelectorAll(selectors: string): NodeListOf<__RipBrowserElement>; closest(selectors: string): __RipBrowserElement | null; };");
|
|
1149
|
-
preamble.push("type __RipDomEl<K extends __RipTag> = Omit<__RipElementMap[K], 'querySelector' | 'querySelectorAll' | 'closest' | 'setAttribute' | 'hidden'> & __RipBrowserElement;");
|
|
1150
|
-
preamble.push("type __RipAttrKeys<T> = { [K in keyof T]-?: K extends 'style' ? never : T[K] extends (...args: any[]) => any ? never : K }[keyof T] & string;");
|
|
1151
|
-
preamble.push('type __RipEvents = { [K in keyof HTMLElementEventMap as `@${K}`]?: ((event: HTMLElementEventMap[K]) => void) | null };');
|
|
1152
|
-
preamble.push('type __RipClassValue = string | boolean | null | undefined | Record<string, boolean> | __RipClassValue[]'); preamble.push('type __RipProps<K extends __RipTag> = { [P in __RipAttrKeys<__RipElementMap[K]>]?: __RipElementMap[K][P] } & __RipEvents & { ref?: string; class?: __RipClassValue | __RipClassValue[]; style?: string; [k: `data-${string}`]: any; [k: `aria-${string}`]: any };');
|
|
1340
|
+
preamble.push(...INTRINSIC_TYPE_DECLS);
|
|
1153
1341
|
}
|
|
1154
1342
|
if (/\bARIA\./.test(source)) {
|
|
1155
|
-
preamble.push(
|
|
1156
|
-
preamble.push("declare const ARIA: {");
|
|
1157
|
-
preamble.push(" bindPopover(open: boolean, popover: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, source?: (() => Element | null | undefined) | null): void;");
|
|
1158
|
-
preamble.push(" bindDialog(open: boolean, dialog: () => Element | null | undefined, setOpen: (isOpen: boolean) => void, dismissable?: boolean): void;");
|
|
1159
|
-
preamble.push(" popupDismiss(open: boolean, popup: () => Element | null | undefined, close: () => void, els?: Array<() => Element | null | undefined>, repos?: (() => void) | null): void;");
|
|
1160
|
-
preamble.push(" popupGuard(delay?: number): any;");
|
|
1161
|
-
preamble.push(" listNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers): void;");
|
|
1162
|
-
preamble.push(" rovingNav(event: KeyboardEvent, handlers: __RipAriaNavHandlers, orientation?: 'vertical' | 'horizontal' | 'both'): void;");
|
|
1163
|
-
preamble.push(" positionBelow(trigger: Element | null | undefined, popup: Element | null | undefined, gap?: number, setVisible?: boolean): void;");
|
|
1164
|
-
preamble.push(" position(trigger: Element | null | undefined, floating: Element | null | undefined, opts?: any): void;");
|
|
1165
|
-
preamble.push(" trapFocus(panel: Element | null | undefined): void;");
|
|
1166
|
-
preamble.push(" wireAria(panel: Element, id: string): void;");
|
|
1167
|
-
preamble.push(" lockScroll(instance: any): void;");
|
|
1168
|
-
preamble.push(" unlockScroll(instance: any): void;");
|
|
1169
|
-
preamble.push(" hasAnchor: boolean;");
|
|
1170
|
-
preamble.push(" [key: string]: any;");
|
|
1171
|
-
preamble.push("};");
|
|
1343
|
+
preamble.push(...ARIA_TYPE_DECLS);
|
|
1172
1344
|
}
|
|
1173
1345
|
if (usesSignal) {
|
|
1174
|
-
preamble.push(
|
|
1175
|
-
preamble.push(
|
|
1346
|
+
preamble.push(SIGNAL_INTERFACE);
|
|
1347
|
+
preamble.push(SIGNAL_FN);
|
|
1176
1348
|
}
|
|
1177
1349
|
if (usesComputed) {
|
|
1178
|
-
preamble.push(
|
|
1179
|
-
preamble.push(
|
|
1350
|
+
preamble.push(COMPUTED_INTERFACE);
|
|
1351
|
+
preamble.push(COMPUTED_FN);
|
|
1180
1352
|
}
|
|
1181
1353
|
if (usesSignal || usesComputed) {
|
|
1182
|
-
preamble.push(
|
|
1354
|
+
preamble.push(EFFECT_FN);
|
|
1183
1355
|
}
|
|
1184
1356
|
if (preamble.length > 0) {
|
|
1185
1357
|
preamble.push('');
|
|
@@ -1253,6 +1425,7 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars, so
|
|
|
1253
1425
|
let exported = false;
|
|
1254
1426
|
let name = null;
|
|
1255
1427
|
let compNode = null;
|
|
1428
|
+
let typeParams = '';
|
|
1256
1429
|
|
|
1257
1430
|
if (head === 'export' && Array.isArray(sexpr[1])) {
|
|
1258
1431
|
exported = true;
|
|
@@ -1260,11 +1433,13 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars, so
|
|
|
1260
1433
|
let innerHead = inner[0]?.valueOf?.() ?? inner[0];
|
|
1261
1434
|
if (innerHead === '=' && Array.isArray(inner[2]) &&
|
|
1262
1435
|
(inner[2][0]?.valueOf?.() ?? inner[2][0]) === 'component') {
|
|
1436
|
+
typeParams = inner[1]?.typeParams || '';
|
|
1263
1437
|
name = inner[1]?.valueOf?.() ?? inner[1];
|
|
1264
1438
|
compNode = inner[2];
|
|
1265
1439
|
}
|
|
1266
1440
|
} else if (head === '=' && Array.isArray(sexpr[2]) &&
|
|
1267
1441
|
(sexpr[2][0]?.valueOf?.() ?? sexpr[2][0]) === 'component') {
|
|
1442
|
+
typeParams = sexpr[1]?.typeParams || '';
|
|
1268
1443
|
name = sexpr[1]?.valueOf?.() ?? sexpr[1];
|
|
1269
1444
|
compNode = sexpr[2];
|
|
1270
1445
|
}
|
|
@@ -1361,7 +1536,7 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars, so
|
|
|
1361
1536
|
}
|
|
1362
1537
|
}
|
|
1363
1538
|
|
|
1364
|
-
lines.push(`${exp}declare class ${name} {`);
|
|
1539
|
+
lines.push(`${exp}declare class ${name}${typeParams} {`);
|
|
1365
1540
|
if (publicProps.length > 0 || inheritedPropsType) {
|
|
1366
1541
|
let propsOpt = hasRequired ? '' : '?';
|
|
1367
1542
|
if (publicProps.length > 0) {
|
package/src/ui.rip
CHANGED
|
@@ -798,6 +798,10 @@ export createRenderer = (opts = {}) ->
|
|
|
798
798
|
mp.appendChild cached._target
|
|
799
799
|
currentComponent = cached
|
|
800
800
|
currentRoute = route.file
|
|
801
|
+
cached.params = params if params
|
|
802
|
+
cached.query = query if query
|
|
803
|
+
cached.mounted() if cached.mounted
|
|
804
|
+
cached.load!(params, query) if cached.load
|
|
801
805
|
else
|
|
802
806
|
pageWrapper = document.createElement('div')
|
|
803
807
|
pageWrapper.setAttribute 'data-component', route.file
|