rip-lang 3.13.77 → 3.13.79
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 +9 -3
- package/docs/dist/rip.js +62 -23
- package/docs/dist/rip.min.js +74 -50
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +5 -2
- package/src/compiler.js +48 -18
- package/src/components.js +33 -7
- package/src/typecheck.js +11 -95
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.79",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
@@ -66,5 +66,8 @@
|
|
|
66
66
|
},
|
|
67
67
|
"homepage": "https://github.com/shreeve/rip-lang#readme",
|
|
68
68
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
69
|
-
"license": "MIT"
|
|
69
|
+
"license": "MIT",
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"typescript": "5.9.3"
|
|
72
|
+
}
|
|
70
73
|
}
|
package/src/compiler.js
CHANGED
|
@@ -671,26 +671,32 @@ export class CodeGenerator {
|
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
-
if (this.
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
} else if (typeof globalThis !== 'undefined' && globalThis.__rip) {
|
|
678
|
-
code += 'const { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
|
|
679
|
-
} else {
|
|
680
|
-
code += this.getReactiveRuntime();
|
|
681
|
-
}
|
|
674
|
+
if (this.options.lspMode && (this.usesReactivity || this.usesTemplates)) {
|
|
675
|
+
if (needsBlank) code += '\n';
|
|
676
|
+
code += getLspRuntimeDeclarations();
|
|
682
677
|
needsBlank = true;
|
|
683
|
-
}
|
|
678
|
+
} else {
|
|
679
|
+
if (this.usesReactivity && !skip) {
|
|
680
|
+
if (skipRT) {
|
|
681
|
+
code += 'var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
|
|
682
|
+
} else if (typeof globalThis !== 'undefined' && globalThis.__rip) {
|
|
683
|
+
code += 'const { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
|
|
684
|
+
} else {
|
|
685
|
+
code += this.getReactiveRuntime();
|
|
686
|
+
}
|
|
687
|
+
needsBlank = true;
|
|
688
|
+
}
|
|
684
689
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
690
|
+
if (this.usesTemplates && !skip) {
|
|
691
|
+
if (skipRT) {
|
|
692
|
+
code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
|
|
693
|
+
} else if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
|
|
694
|
+
code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
|
|
695
|
+
} else {
|
|
696
|
+
code += this.getComponentRuntime();
|
|
697
|
+
}
|
|
698
|
+
needsBlank = true;
|
|
692
699
|
}
|
|
693
|
-
needsBlank = true;
|
|
694
700
|
}
|
|
695
701
|
|
|
696
702
|
if (this.dataSection !== null && this.dataSection !== undefined && !skip) {
|
|
@@ -3289,12 +3295,15 @@ export class Compiler {
|
|
|
3289
3295
|
sourceMap = new SourceMapGenerator(file, sourceFile, source);
|
|
3290
3296
|
}
|
|
3291
3297
|
|
|
3298
|
+
let lspMode = this.options.mode === 'lsp';
|
|
3299
|
+
|
|
3292
3300
|
let generator = new CodeGenerator({
|
|
3293
3301
|
dataSection,
|
|
3294
|
-
skipPreamble: this.options.skipPreamble,
|
|
3302
|
+
skipPreamble: lspMode || this.options.skipPreamble,
|
|
3295
3303
|
skipRuntimes: this.options.skipRuntimes,
|
|
3296
3304
|
skipExports: this.options.skipExports,
|
|
3297
3305
|
reactiveVars: this.options.reactiveVars,
|
|
3306
|
+
lspMode,
|
|
3298
3307
|
sourceMap,
|
|
3299
3308
|
});
|
|
3300
3309
|
let code = generator.compile(sexpr);
|
|
@@ -3313,6 +3322,11 @@ export class Compiler {
|
|
|
3313
3322
|
dts = emitTypes(typeTokens, sexpr);
|
|
3314
3323
|
}
|
|
3315
3324
|
|
|
3325
|
+
// LSP mode: prepend DTS to code for a single combined output
|
|
3326
|
+
if (lspMode && dts) {
|
|
3327
|
+
code = dts.trimEnd() + '\n\n' + code;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3316
3330
|
return { tokens, sexpr, code, dts, map, reverseMap, data: dataSection, reactiveVars: generator.reactiveVars };
|
|
3317
3331
|
}
|
|
3318
3332
|
|
|
@@ -3374,4 +3388,20 @@ export function getComponentRuntime() {
|
|
|
3374
3388
|
return new CodeGenerator({}).getComponentRuntime();
|
|
3375
3389
|
}
|
|
3376
3390
|
|
|
3391
|
+
export function getLspRuntimeDeclarations() {
|
|
3392
|
+
return `\
|
|
3393
|
+
interface Signal<T> { value: T; read(): T; }
|
|
3394
|
+
interface Computed<T> { readonly value: T; read(): T; }
|
|
3395
|
+
declare function __state<T>(v: T): Signal<T>;
|
|
3396
|
+
declare function __computed<T>(fn: () => T): Computed<T>;
|
|
3397
|
+
declare function __effect(fn: () => void | (() => void)): () => void;
|
|
3398
|
+
declare function __batch<T>(fn: () => T): T;
|
|
3399
|
+
declare function __readonly<T>(v: T): Readonly<{ value: T }>;
|
|
3400
|
+
declare function setContext(key: string, value: any): void;
|
|
3401
|
+
declare function getContext(key: string): any;
|
|
3402
|
+
declare function hasContext(key: string): boolean;
|
|
3403
|
+
declare class __Component { constructor(props?: any); [key: string]: any; }
|
|
3404
|
+
`;
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3377
3407
|
export { formatSExpr };
|
package/src/components.js
CHANGED
|
@@ -91,9 +91,10 @@ function extractInputType(pairs) {
|
|
|
91
91
|
* Handles both [".", "this", name] (@property) and plain string.
|
|
92
92
|
*/
|
|
93
93
|
function getMemberName(target) {
|
|
94
|
-
if (typeof target === 'string') return target;
|
|
95
|
-
if (Array.isArray(target) && target[0] === '.' && target[1] === 'this' &&
|
|
96
|
-
|
|
94
|
+
if (typeof target === 'string' || target instanceof String) return target.valueOf();
|
|
95
|
+
if (Array.isArray(target) && target[0] === '.' && target[1] === 'this' &&
|
|
96
|
+
(typeof target[2] === 'string' || target[2] instanceof String)) {
|
|
97
|
+
return target[2].valueOf();
|
|
97
98
|
}
|
|
98
99
|
return null;
|
|
99
100
|
}
|
|
@@ -106,6 +107,16 @@ function isPublicProp(target) {
|
|
|
106
107
|
return Array.isArray(target) && target[0] === '.' && target[1] === 'this';
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Extract type annotation from s-expression target node.
|
|
112
|
+
* Type annotations are stored as .type on String objects by the type rewriter.
|
|
113
|
+
*/
|
|
114
|
+
function getMemberType(target) {
|
|
115
|
+
if (target instanceof String && target.type) return target.type;
|
|
116
|
+
if (Array.isArray(target) && target[2] instanceof String && target[2].type) return target[2].type;
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
// ============================================================================
|
|
110
121
|
// Prototype Installation
|
|
111
122
|
// ============================================================================
|
|
@@ -702,7 +713,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
702
713
|
} else if (op === 'state') {
|
|
703
714
|
const varName = getMemberName(stmt[1]);
|
|
704
715
|
if (varName) {
|
|
705
|
-
stateVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]) });
|
|
716
|
+
stateVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]), type: getMemberType(stmt[1]) });
|
|
706
717
|
memberNames.add(varName);
|
|
707
718
|
reactiveMembers.add(varName);
|
|
708
719
|
}
|
|
@@ -716,7 +727,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
716
727
|
} else if (op === 'readonly') {
|
|
717
728
|
const varName = getMemberName(stmt[1]);
|
|
718
729
|
if (varName) {
|
|
719
|
-
readonlyVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]) });
|
|
730
|
+
readonlyVars.push({ name: varName, value: stmt[2], isPublic: isPublicProp(stmt[1]), type: getMemberType(stmt[1]) });
|
|
720
731
|
memberNames.add(varName);
|
|
721
732
|
}
|
|
722
733
|
} else if (op === '=') {
|
|
@@ -777,7 +788,22 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
777
788
|
lines.push('class extends __Component {');
|
|
778
789
|
|
|
779
790
|
// --- Init (called by __Component constructor) ---
|
|
780
|
-
|
|
791
|
+
if (this.options.lspMode) {
|
|
792
|
+
const typedProps = [];
|
|
793
|
+
for (const v of [...readonlyVars, ...stateVars]) {
|
|
794
|
+
if (v.isPublic) {
|
|
795
|
+
const t = v.type || 'any';
|
|
796
|
+
typedProps.push(`${v.name}?: ${t}`);
|
|
797
|
+
typedProps.push(`__bind_${v.name}__?: ${t}`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const propsType = typedProps.length > 0
|
|
801
|
+
? `{ ${typedProps.join(', ')}, [key: string]: any }`
|
|
802
|
+
: 'any';
|
|
803
|
+
lines.push(` _init(props: ${propsType}) {`);
|
|
804
|
+
} else {
|
|
805
|
+
lines.push(' _init(props) {');
|
|
806
|
+
}
|
|
781
807
|
|
|
782
808
|
// Constants (readonly)
|
|
783
809
|
for (const { name, value, isPublic } of readonlyVars) {
|
|
@@ -858,7 +884,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
858
884
|
}
|
|
859
885
|
|
|
860
886
|
// --- Render block (fine-grained) ---
|
|
861
|
-
if (renderBlock) {
|
|
887
|
+
if (renderBlock && !this.options.lspMode) {
|
|
862
888
|
const renderBody = renderBlock[1];
|
|
863
889
|
const result = this.buildRender(renderBody);
|
|
864
890
|
|
package/src/typecheck.js
CHANGED
|
@@ -36,8 +36,11 @@ export function fromVirtual(p) { return p.endsWith('.rip.ts') ? p.slice(0, -3) :
|
|
|
36
36
|
// TS error codes to skip — Rip resolves modules differently and
|
|
37
37
|
// treats async return types transparently.
|
|
38
38
|
export const SKIP_CODES = new Set([
|
|
39
|
-
|
|
39
|
+
2300, // Duplicate identifier (DTS declarations coexist with compiled class bodies)
|
|
40
40
|
2304, // Cannot find name
|
|
41
|
+
2307, // Cannot find module
|
|
42
|
+
2393, // Duplicate function implementation
|
|
43
|
+
2451, // Cannot redeclare block-scoped variable
|
|
41
44
|
1064, // Return type of async function must be Promise
|
|
42
45
|
2582, // Cannot find name 'test' (test runner globals)
|
|
43
46
|
2593, // Cannot find name 'describe' (test runner globals)
|
|
@@ -61,100 +64,12 @@ export function createTypeCheckSettings(ts, overrides = {}) {
|
|
|
61
64
|
|
|
62
65
|
// ── Shared compilation pipeline ────────────────────────────────────
|
|
63
66
|
|
|
64
|
-
// Compile a .rip file for type-checking.
|
|
65
|
-
//
|
|
66
|
-
// source maps
|
|
67
|
+
// Compile a .rip file for type-checking. Uses mode: 'lsp' which emits
|
|
68
|
+
// DTS + typed runtime declarations + compiled code in a single output.
|
|
69
|
+
// Builds bidirectional source maps for the LSP and CLI checker.
|
|
67
70
|
export function compileForCheck(filePath, source, compiler) {
|
|
68
|
-
const result = compiler.compile(source, { sourceMap: true, types:
|
|
71
|
+
const result = compiler.compile(source, { sourceMap: true, types: 'emit', mode: 'lsp' });
|
|
69
72
|
let code = result.code || '';
|
|
70
|
-
let dts = result.dts ? result.dts.trimEnd() + '\n' : '';
|
|
71
|
-
|
|
72
|
-
// Strip .d.ts imports — compiled JS already has them
|
|
73
|
-
dts = dts.replace(/^import\s.*;\s*\n/gm, '');
|
|
74
|
-
|
|
75
|
-
// Extract well-formed function signatures and merge into JS.
|
|
76
|
-
// Leaving them as bare declarations causes TypeScript to treat
|
|
77
|
-
// them as overload signatures that conflict with the implementations.
|
|
78
|
-
const funcSigs = new Map();
|
|
79
|
-
dts = dts.replace(
|
|
80
|
-
/^(?:export|declare)\s+function\s+(\w+)\(([^)]*)\):\s*(.+);\s*$/gm,
|
|
81
|
-
(_m, name, params, ret) => { funcSigs.set(name, { params, ret }); return ''; },
|
|
82
|
-
);
|
|
83
|
-
dts = dts.replace(/^\s*\n/gm, '');
|
|
84
|
-
|
|
85
|
-
// Strip remaining malformed multi-line declarations
|
|
86
|
-
dts = dts.replace(/(?:export|declare)\s+function\s+\w+\([\s\S]*?\);\s*/g, '');
|
|
87
|
-
dts = dts.replace(/^\s*\n/gm, '');
|
|
88
|
-
|
|
89
|
-
for (const [name, { params, ret }] of funcSigs) {
|
|
90
|
-
const paramTypes = new Map();
|
|
91
|
-
if (params.trim()) {
|
|
92
|
-
for (const p of params.split(',')) {
|
|
93
|
-
const colon = p.indexOf(':');
|
|
94
|
-
if (colon !== -1) paramTypes.set(p.slice(0, colon).trim(), p.slice(colon + 1).trim());
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const funcRe = new RegExp(
|
|
98
|
-
`((?:export\\s+)?(?:async\\s+)?function\\s+${name})\\(([^)]*)\\)(\\s*\\{)`,
|
|
99
|
-
);
|
|
100
|
-
code = code.replace(funcRe, (_match, prefix, codeParams, brace) => {
|
|
101
|
-
const typed = codeParams.split(',').map(p => {
|
|
102
|
-
const n = p.trim();
|
|
103
|
-
const t = paramTypes.get(n);
|
|
104
|
-
return t ? `${n}: ${t}` : n;
|
|
105
|
-
}).join(', ');
|
|
106
|
-
return `${prefix}(${typed}): ${ret}${brace}`;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Extract reactive const declarations (state, computed, readonly, effect)
|
|
111
|
-
// from DTS and merge their types into the code — same pattern as functions.
|
|
112
|
-
// DTS: `declare const clicks: Signal<number>;` → removed
|
|
113
|
-
// Code: `const clicks = __state(0);` → `const clicks: Signal<number> = __state(0);`
|
|
114
|
-
const reactiveConsts = new Map();
|
|
115
|
-
dts = dts.replace(
|
|
116
|
-
/^(?:export\s+)?(?:declare\s+)?const\s+(\w+)\s*:\s*(.+?);\s*$/gm,
|
|
117
|
-
(_m, name, type) => { reactiveConsts.set(name, type); return ''; },
|
|
118
|
-
);
|
|
119
|
-
dts = dts.replace(/^\s*\n/gm, '');
|
|
120
|
-
|
|
121
|
-
for (const [name, type] of reactiveConsts) {
|
|
122
|
-
code = code.replace(
|
|
123
|
-
new RegExp(`(const\\s+${name})\\s*=`),
|
|
124
|
-
(_, prefix) => `${prefix}: ${type} =`,
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Remove component class implementations when the DTS already has
|
|
129
|
-
// typed class declarations — avoids "Duplicate identifier" conflicts.
|
|
130
|
-
for (const m of dts.matchAll(/^(?:export\s+)?declare\s+class\s+(\w+)\b/gm)) {
|
|
131
|
-
const name = m[1];
|
|
132
|
-
const re = new RegExp(`^export\\s+const\\s+${name}\\s*=\\s*class\\s+extends\\s+\\w+`);
|
|
133
|
-
const lines = code.split('\n');
|
|
134
|
-
for (let i = 0; i < lines.length; i++) {
|
|
135
|
-
if (!re.test(lines[i])) continue;
|
|
136
|
-
let depth = 0, start = i;
|
|
137
|
-
for (; i < lines.length; i++) {
|
|
138
|
-
for (const ch of lines[i]) { if (ch === '{') depth++; else if (ch === '}') depth--; }
|
|
139
|
-
if (depth <= 0) break;
|
|
140
|
-
}
|
|
141
|
-
lines.splice(start, i - start + 1);
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
code = lines.join('\n');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Remove bare `let x;` declarations when the DTS already declares
|
|
148
|
-
// `let x: Type;` — avoids "Cannot redeclare" conflicts. Handles
|
|
149
|
-
// both single (`let x;`) and comma-separated (`let x, y;`) forms.
|
|
150
|
-
const dtsVars = new Set();
|
|
151
|
-
for (const m of dts.matchAll(/^(?:let|var)\s+(\w+)\s*:/gm)) dtsVars.add(m[1]);
|
|
152
|
-
if (dtsVars.size) {
|
|
153
|
-
code = code.replace(/^(let|var)\s+([\w\s,]+);[ \t]*$/gm, (_m, kw, vars) => {
|
|
154
|
-
const kept = vars.split(',').map(v => v.trim()).filter(v => !dtsVars.has(v));
|
|
155
|
-
return kept.length ? `${kw} ${kept.join(', ')};` : '';
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
73
|
|
|
159
74
|
// Determine if this file should be type-checked
|
|
160
75
|
const hasOwnTypes = hasTypeAnnotations(source);
|
|
@@ -175,8 +90,9 @@ export function compileForCheck(filePath, source, compiler) {
|
|
|
175
90
|
// Ensure every file is treated as a module (not a global script)
|
|
176
91
|
if (!/\bexport\b/.test(code) && !/\bimport\b/.test(code)) code += '\nexport {};\n';
|
|
177
92
|
|
|
178
|
-
const
|
|
179
|
-
const
|
|
93
|
+
const dts = result.dts ? result.dts.trimEnd() + '\n' : '';
|
|
94
|
+
const tsContent = code;
|
|
95
|
+
const headerLines = hasTypes && dts ? countLines(dts + '\n') : 1;
|
|
180
96
|
|
|
181
97
|
// Build bidirectional line maps
|
|
182
98
|
const { srcToGen, genToSrc } = buildLineMap(result.reverseMap, result.map, headerLines);
|