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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.77",
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.usesReactivity && !skip) {
675
- if (skipRT) {
676
- code += 'var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
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
- if (this.usesTemplates && !skip) {
686
- if (skipRT) {
687
- code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
688
- } else if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
689
- code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __transition, __handleComponentError, __Component } = globalThis.__ripComponent;\n';
690
- } else {
691
- code += this.getComponentRuntime();
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' && typeof target[2] === 'string') {
96
- return target[2];
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
- lines.push(' _init(props) {');
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
- 2307, // Cannot find module
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. Merges .d.ts declarations into
65
- // the compiled JS, detects type annotations, and builds bidirectional
66
- // source maps. Returns everything both the CLI and LSP need.
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: true, skipPreamble: true });
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 tsContent = (hasTypes ? dts + '\n' : '') + code;
179
- const headerLines = hasTypes ? countLines(dts + '\n') : 1;
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);