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.
- package/CHANGELOG.md +2 -2
- package/README.md +1 -1
- package/bin/rip +14 -18
- package/docs/dist/rip-ui.min.js +105 -104
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +95 -94
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/package.json +1 -1
- package/src/compiler.js +20 -51
- package/src/components.js +8 -2
- package/src/typecheck.js +38 -1
- package/src/types.js +30 -38
|
Binary file
|
package/package.json
CHANGED
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
|
|
281
|
-
if (this.sourceMap) this.buildMappings(
|
|
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
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
buildMappings(
|
|
290
|
-
if (!
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
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('}'))
|
|
616
|
+
if (!blockStmts.includes(h) || !generated.endsWith('}')) generated += ';';
|
|
650
617
|
}
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
692
|
-
|
|
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
|
|
1006
|
-
let
|
|
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
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
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
|
|