rip-lang 3.10.11 → 3.10.14

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.10.11",
3
+ "version": "3.10.14",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/compiler.js CHANGED
@@ -277,56 +277,23 @@ export class CodeGenerator {
277
277
  this.collectProgramVariables(sexpr);
278
278
  let code = this.generate(sexpr);
279
279
 
280
- // Build source map mappings from generated code + S-expression locations
281
- if (this.sourceMap) this.buildMappings(code, sexpr);
280
+ // Build source map mappings from generation-time recorded entries
281
+ if (this.sourceMap) this.buildMappings();
282
282
 
283
283
  return code;
284
284
  }
285
285
 
286
- // Build source map by walking generated output lines and S-expression nodes.
287
- // Collects locations from STATEMENT-level nodes only (direct children of
288
- // program/block containers), then matches them to output lines in order.
289
- buildMappings(code, sexpr) {
290
- if (!sexpr || sexpr[0] !== 'program') return;
291
-
292
- // Collect statement locations: only direct children of program/block nodes.
293
- // This gives exactly one loc per output statement line, in source order.
294
- let locs = [];
295
- let collect = (node) => {
296
- if (!Array.isArray(node)) return;
297
- let head = node[0];
298
- if (head === 'program' || head === 'block') {
299
- for (let i = 1; i < node.length; i++) {
300
- let child = node[i];
301
- if (Array.isArray(child) && child.loc) locs.push(child.loc);
302
- collect(child);
303
- }
304
- } else {
305
- for (let i = 1; i < node.length; i++) collect(node[i]);
306
- }
307
- };
308
- collect(sexpr);
309
-
310
- // Match output lines to collected statement locations in parallel.
311
- // Skip lines with no source correspondence (preamble, braces, etc.).
312
- let lines = code.split('\n');
313
- let locIdx = 0;
314
- for (let outLine = 0; outLine < lines.length; outLine++) {
315
- let line = lines[outLine];
316
- let trimmed = line.trim();
317
-
318
- if (!trimmed || trimmed === '}' || trimmed === '});') continue;
319
- if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
320
- if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toMatchable')) continue;
321
- if (trimmed.startsWith('const {') && trimmed.includes('__')) continue;
322
- if (trimmed.startsWith('} else')) continue;
323
- if (trimmed.startsWith('//# source')) continue;
324
-
325
- if (locIdx < locs.length) {
326
- let indent = line.length - trimmed.length;
327
- this.sourceMap.addMapping(outLine, indent, locs[locIdx].r, locs[locIdx].c);
328
- locIdx++;
286
+ // Build source map from generation-time recorded entries.
287
+ // Each entry pairs a statement's generated code with its source loc.
288
+ // Output line positions are computed by exact arithmetic no heuristics.
289
+ buildMappings() {
290
+ if (!this._stmtEntries) return;
291
+ let lineOffset = this._preambleLines;
292
+ for (let entry of this._stmtEntries) {
293
+ if (entry.loc) {
294
+ this.sourceMap.addMapping(lineOffset, 0, entry.loc.r, entry.loc.c);
329
295
  }
296
+ lineOffset += entry.code.split('\n').length;
330
297
  }
331
298
  }
332
299
 
@@ -629,7 +596,7 @@ export class CodeGenerator {
629
596
 
630
597
  // Generate body first to detect needed helpers
631
598
  let blockStmts = ['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'];
632
- let statementsCode = other.map((stmt, index) => {
599
+ let stmtEntries = other.map((stmt, index) => {
633
600
  let isSingle = other.length === 1 && imports.length === 0 && exports.length === 0;
634
601
  let isObj = this.is(stmt, 'object');
635
602
  let isObjComp = isObj && stmt.length === 2 && Array.isArray(stmt[1]) && Array.isArray(stmt[1][1]) && stmt[1][1][0] === 'comprehension';
@@ -646,10 +613,12 @@ export class CodeGenerator {
646
613
 
647
614
  if (generated && !generated.endsWith(';')) {
648
615
  let h = Array.isArray(stmt) ? stmt[0] : null;
649
- if (!blockStmts.includes(h) || !generated.endsWith('}')) return generated + ';';
616
+ if (!blockStmts.includes(h) || !generated.endsWith('}')) generated += ';';
650
617
  }
651
- return generated;
652
- }).join('\n');
618
+ let loc = Array.isArray(stmt) ? stmt.loc : null;
619
+ return { code: generated, loc };
620
+ });
621
+ let statementsCode = stmtEntries.map(e => e.code).join('\n');
653
622
 
654
623
  let needsBlank = false;
655
624
 
@@ -719,6 +688,8 @@ export class CodeGenerator {
719
688
  }
720
689
 
721
690
  if (needsBlank && code.length > 0) code += '\n';
691
+ this._stmtEntries = stmtEntries;
692
+ this._preambleLines = code.length === 0 ? 0 : code.split('\n').length - 1;
722
693
  code += statementsCode;
723
694
  code += exportsCode;
724
695
 
@@ -926,9 +897,7 @@ export class CodeGenerator {
926
897
  let mapped = this._atParamMap.get(str(prop));
927
898
  if (mapped) return mapped;
928
899
  }
929
- this.suppressReactiveUnwrap = true;
930
900
  let objCode = this.generate(obj, 'value');
931
- this.suppressReactiveUnwrap = false;
932
901
  let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
933
902
  objCode.startsWith('await ') ||
934
903
  ((this.is(obj, 'object') || this.is(obj, 'yield')));
package/src/components.js CHANGED
@@ -688,8 +688,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
688
688
  // Effects
689
689
  for (const effect of effects) {
690
690
  const effectBody = effect[2];
691
- const effectCode = this.generateInComponent(effectBody, 'value');
692
- lines.push(` __effect(() => { ${effectCode}; });`);
691
+ if (this.is(effectBody, 'block') && effectBody.length > 2) {
692
+ const transformed = this.transformComponentMembers(effectBody);
693
+ const body = this.generateFunctionBody(transformed, [], true);
694
+ lines.push(` __effect(() => ${body});`);
695
+ } else {
696
+ const effectCode = this.generateInComponent(effectBody, 'value');
697
+ lines.push(` __effect(() => { ${effectCode}; });`);
698
+ }
693
699
  }
694
700
 
695
701
  lines.push(' }');
package/src/typecheck.js CHANGED
@@ -65,7 +65,7 @@ export function createTypeCheckSettings(ts, overrides = {}) {
65
65
  // the compiled JS, detects type annotations, and builds bidirectional
66
66
  // source maps. Returns everything both the CLI and LSP need.
67
67
  export function compileForCheck(filePath, source, compiler) {
68
- const result = compiler.compile(source, { sourceMap: true, types: true });
68
+ const result = compiler.compile(source, { sourceMap: true, types: true, skipPreamble: true });
69
69
  let code = result.code || '';
70
70
  let dts = result.dts ? result.dts.trimEnd() + '\n' : '';
71
71
 
@@ -107,6 +107,43 @@ export function compileForCheck(filePath, source, compiler) {
107
107
  });
108
108
  }
109
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
+
110
147
  // Remove bare `let x;` declarations when the DTS already declares
111
148
  // `let x: Type;` — avoids "Cannot redeclare" conflicts. Handles
112
149
  // both single (`let x;`) and comma-separated (`let x, y;`) forms.
package/src/types.js CHANGED
@@ -1002,55 +1002,47 @@ function emitComponentTypes(sexpr, lines, indent, indentLevel, componentVars) {
1002
1002
  let members = (Array.isArray(body) && (body[0]?.valueOf?.() ?? body[0]) === 'block')
1003
1003
  ? body.slice(1) : (body ? [body] : []);
1004
1004
 
1005
- let props = [];
1006
- let methods = [];
1005
+ let publicProps = [];
1006
+ let hasRequired = false;
1007
1007
 
1008
1008
  for (let member of members) {
1009
1009
  if (!Array.isArray(member)) continue;
1010
1010
  let mHead = member[0]?.valueOf?.() ?? member[0];
1011
1011
 
1012
- // Reactive state: ["state", "count", 0]
1013
- if (mHead === 'state') {
1014
- let propName = member[1]?.valueOf?.() ?? member[1];
1015
- let type = member[1]?.type;
1016
- props.push(` ${propName}: ${type ? expandSuffixes(type) : 'any'};`);
1017
- componentVars.add(propName);
1018
- }
1019
- // Computed: ["computed", "doubled", expr]
1020
- else if (mHead === 'computed') {
1021
- let propName = member[1]?.valueOf?.() ?? member[1];
1022
- let type = member[1]?.type;
1023
- props.push(` readonly ${propName}: ${type ? expandSuffixes(type) : 'any'};`);
1012
+ let target, propName, isProp, type, hasDefault;
1013
+
1014
+ if (mHead === 'state' || mHead === 'readonly' || mHead === 'computed') {
1015
+ target = member[1];
1016
+ isProp = Array.isArray(target) && (target[0]?.valueOf?.() ?? target[0]) === '.' && (target[1]?.valueOf?.() ?? target[1]) === 'this';
1017
+ propName = isProp ? (target[2]?.valueOf?.() ?? target[2]) : (target?.valueOf?.() ?? target);
1018
+ type = isProp ? target[2]?.type : target?.type;
1019
+ hasDefault = true;
1024
1020
  componentVars.add(propName);
1025
- }
1026
- // Method object: ["object", ["methodName", ["->", params, body], ":"]]
1027
- else if (mHead === 'object') {
1028
- for (let j = 1; j < member.length; j++) {
1029
- let entry = member[j];
1030
- if (!Array.isArray(entry)) continue;
1031
- let methodName = entry[0]?.valueOf?.() ?? entry[0];
1032
- if (methodName === 'render') continue; // skip render
1033
- let fn = entry[1];
1034
- if (Array.isArray(fn)) {
1035
- let fnHead = fn[0]?.valueOf?.() ?? fn[0];
1036
- if (fnHead === '->' || fnHead === '=>') {
1037
- methods.push(` ${methodName}(): void;`);
1038
- }
1039
- }
1040
- }
1041
- }
1042
- // Skip render blocks
1043
- else if (mHead === 'render') {
1021
+ } else if (mHead === '.') {
1022
+ isProp = (member[1]?.valueOf?.() ?? member[1]) === 'this';
1023
+ propName = isProp ? (member[2]?.valueOf?.() ?? member[2]) : null;
1024
+ type = isProp ? member[2]?.type : null;
1025
+ hasDefault = false;
1026
+ if (propName) componentVars.add(propName);
1027
+ } else {
1044
1028
  continue;
1045
1029
  }
1030
+
1031
+ if (!isProp || !propName) continue;
1032
+
1033
+ let typeStr = type ? expandSuffixes(type) : 'any';
1034
+ let opt = hasDefault ? '?' : '';
1035
+ if (!hasDefault) hasRequired = true;
1036
+ publicProps.push(` ${propName}${opt}: ${typeStr};`);
1046
1037
  }
1047
1038
 
1048
1039
  lines.push(`${exp}declare class ${name} {`);
1049
- lines.push(` constructor(props?: Record<string, any>);`);
1050
- for (let p of props) lines.push(p);
1051
- for (let m of methods) lines.push(m);
1052
- lines.push(` mount(target: Element | string): ${name};`);
1053
- lines.push(` unmount(): void;`);
1040
+ if (publicProps.length > 0) {
1041
+ let propsOpt = hasRequired ? '' : '?';
1042
+ lines.push(` constructor(props${propsOpt}: {`);
1043
+ for (let p of publicProps) lines.push(p);
1044
+ lines.push(` });`);
1045
+ }
1054
1046
  lines.push(`}`);
1055
1047
  }
1056
1048