rip-lang 3.13.55 → 3.13.56
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 +4 -2
- package/bin/rip +26 -15
- package/docs/RIP-LANG.md +38 -10
- package/docs/dist/rip.js +292 -278
- package/docs/dist/rip.min.js +103 -103
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/app.rip +1 -1
- package/src/browser.js +13 -4
- package/src/compiler.js +29 -14
- package/src/components.js +25 -15
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/app.rip
CHANGED
package/src/browser.js
CHANGED
|
@@ -72,14 +72,15 @@ async function processRipScripts() {
|
|
|
72
72
|
for (const s of sources) {
|
|
73
73
|
if (!s.code) continue;
|
|
74
74
|
try {
|
|
75
|
-
|
|
75
|
+
const js = compileToJS(s.code, opts);
|
|
76
|
+
compiled.push({ js, url: s.url || 'inline' });
|
|
76
77
|
} catch (e) {
|
|
77
|
-
console.error(
|
|
78
|
+
console.error(`Rip compile error in ${s.url || 'inline'}:`, e.message);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
if (compiled.length > 0) {
|
|
82
|
-
let js = compiled.join('\n');
|
|
83
|
+
let js = compiled.map(c => c.js).join('\n');
|
|
83
84
|
|
|
84
85
|
// Step 4: Append data-mount call inside the shared IIFE
|
|
85
86
|
const mount = runtimeTag?.getAttribute('data-mount');
|
|
@@ -91,7 +92,15 @@ async function processRipScripts() {
|
|
|
91
92
|
try {
|
|
92
93
|
await (0, eval)(`(async()=>{\n${js}\n})()`);
|
|
93
94
|
} catch (e) {
|
|
94
|
-
|
|
95
|
+
if (e instanceof SyntaxError) {
|
|
96
|
+
console.error(`Rip syntax error in combined output: ${e.message}`);
|
|
97
|
+
for (const c of compiled) {
|
|
98
|
+
try { new Function(`(async()=>{\n${c.js}\n})()`); }
|
|
99
|
+
catch (e2) { console.error(` → source: ${c.url}`, e2.message); }
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
console.error('Rip runtime error:', e);
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
}
|
package/src/compiler.js
CHANGED
|
@@ -588,26 +588,25 @@ export class CodeGenerator {
|
|
|
588
588
|
|
|
589
589
|
generateProgram(head, statements, context, sexpr) {
|
|
590
590
|
let code = '';
|
|
591
|
-
let imports = [],
|
|
591
|
+
let imports = [], body = [];
|
|
592
592
|
|
|
593
593
|
for (let stmt of statements) {
|
|
594
|
-
if (!Array.isArray(stmt)) {
|
|
594
|
+
if (!Array.isArray(stmt)) { body.push(stmt); continue; }
|
|
595
595
|
let h = stmt[0];
|
|
596
596
|
if (h === 'import') imports.push(stmt);
|
|
597
|
-
else
|
|
598
|
-
else other.push(stmt);
|
|
597
|
+
else body.push(stmt);
|
|
599
598
|
}
|
|
600
599
|
|
|
601
600
|
// Generate body first to detect needed helpers
|
|
602
601
|
let blockStmts = ['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'];
|
|
603
|
-
let stmtEntries =
|
|
604
|
-
let isSingle =
|
|
602
|
+
let stmtEntries = body.map((stmt, index) => {
|
|
603
|
+
let isSingle = body.length === 1 && imports.length === 0;
|
|
605
604
|
let isObj = this.is(stmt, 'object');
|
|
606
605
|
let isObjComp = isObj && stmt.length === 2 && Array.isArray(stmt[1]) && Array.isArray(stmt[1][1]) && stmt[1][1][0] === 'comprehension';
|
|
607
606
|
let isAlreadyExpr = (this.is(stmt, 'comprehension') || this.is(stmt, 'object-comprehension') || this.is(stmt, 'do-iife'));
|
|
608
607
|
let hasNoVars = this.programVars.size === 0;
|
|
609
608
|
let needsParens = isSingle && isObj && hasNoVars && !isAlreadyExpr && !isObjComp;
|
|
610
|
-
let isLast = index ===
|
|
609
|
+
let isLast = index === body.length - 1;
|
|
611
610
|
let isLastComp = isLast && isAlreadyExpr;
|
|
612
611
|
|
|
613
612
|
let generated;
|
|
@@ -672,12 +671,6 @@ export class CodeGenerator {
|
|
|
672
671
|
}
|
|
673
672
|
}
|
|
674
673
|
|
|
675
|
-
// Generate exports code early so component/reactivity flags are set before runtime checks
|
|
676
|
-
let exportsCode = '';
|
|
677
|
-
if (exports.length > 0) {
|
|
678
|
-
exportsCode = '\n' + exports.map(s => this.addSemicolon(s, this.generate(s, 'statement'))).join('\n');
|
|
679
|
-
}
|
|
680
|
-
|
|
681
674
|
if (this.usesReactivity && !skip) {
|
|
682
675
|
if (skipRT) {
|
|
683
676
|
code += 'var { __state, __computed, __effect, __batch, __readonly, __setErrorHandler, __handleError, __catchErrors } = globalThis.__rip;\n';
|
|
@@ -709,7 +702,6 @@ export class CodeGenerator {
|
|
|
709
702
|
this._stmtEntries = stmtEntries;
|
|
710
703
|
this._preambleLines = code.length === 0 ? 0 : code.split('\n').length - 1;
|
|
711
704
|
code += statementsCode;
|
|
712
|
-
code += exportsCode;
|
|
713
705
|
|
|
714
706
|
if (this.dataSection !== null && this.dataSection !== undefined) {
|
|
715
707
|
code += `\n\nfunction _setDataSection() {\n DATA = ${JSON.stringify(this.dataSection)};\n}`;
|
|
@@ -786,6 +778,18 @@ export class CodeGenerator {
|
|
|
786
778
|
let [target, value] = rest;
|
|
787
779
|
let op = head === '?=' ? '??=' : head;
|
|
788
780
|
|
|
781
|
+
// Optional chain assignment: x?.prop = val → if (x != null) x.prop = val
|
|
782
|
+
let optInfo = this._findOptionalInTarget(target);
|
|
783
|
+
if (optInfo) {
|
|
784
|
+
let guardCode = this.generate(optInfo.guard, 'value');
|
|
785
|
+
let targetCode = this.generate(optInfo.rewritten, 'value');
|
|
786
|
+
let valueCode = this.generate(value, 'value');
|
|
787
|
+
if (context === 'value') {
|
|
788
|
+
return `(${guardCode} != null ? (${targetCode} ${op} ${valueCode}) : undefined)`;
|
|
789
|
+
}
|
|
790
|
+
return `if (${guardCode} != null) ${targetCode} ${op} ${valueCode}`;
|
|
791
|
+
}
|
|
792
|
+
|
|
789
793
|
// Validate: no sigils in assignment targets (except void function syntax)
|
|
790
794
|
let isFnValue = (this.is(value, '->') || this.is(value, '=>') || this.is(value, 'def'));
|
|
791
795
|
if (target instanceof String && meta(target, 'await') !== undefined && !isFnValue) {
|
|
@@ -2754,6 +2758,17 @@ export class CodeGenerator {
|
|
|
2754
2758
|
return code;
|
|
2755
2759
|
}
|
|
2756
2760
|
|
|
2761
|
+
_findOptionalInTarget(node) {
|
|
2762
|
+
if (!Array.isArray(node)) return null;
|
|
2763
|
+
if (node[0] === '?.') return { guard: node[1], rewritten: ['.', node[1], node[2]] };
|
|
2764
|
+
if (node[0] === 'optindex') return { guard: node[1], rewritten: ['[]', node[1], node[2]] };
|
|
2765
|
+
if (node[0] === '.' || node[0] === '[]') {
|
|
2766
|
+
let inner = this._findOptionalInTarget(node[1]);
|
|
2767
|
+
if (inner) return { guard: inner.guard, rewritten: [node[0], inner.rewritten, node[2]] };
|
|
2768
|
+
}
|
|
2769
|
+
return null;
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2757
2772
|
unwrapLogical(code) {
|
|
2758
2773
|
if (typeof code !== 'string') return code;
|
|
2759
2774
|
while (code.startsWith('(') && code.endsWith(')')) {
|
package/src/components.js
CHANGED
|
@@ -141,6 +141,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
141
141
|
// - Event modifiers: @click.prevent: → [@click.prevent]:
|
|
142
142
|
// - Dynamic classes: div.('card', x && 'active') → div.__clsx(...)
|
|
143
143
|
// - Implicit nesting: inject -> before INDENT for template elements
|
|
144
|
+
// - Data attribute sigil: $open: true → "data-open": true
|
|
144
145
|
// - Hyphenated attributes: data-foo: "x" → "data-foo": "x"
|
|
145
146
|
// ==========================================================================
|
|
146
147
|
|
|
@@ -272,9 +273,20 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
272
273
|
return 1;
|
|
273
274
|
}
|
|
274
275
|
|
|
276
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
277
|
+
// Data attribute sigil
|
|
278
|
+
// $open: true → "data-open": true
|
|
279
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
280
|
+
if (tag === 'PROPERTY' && token[1][0] === '$' && token[1].length > 1) {
|
|
281
|
+
token[0] = 'STRING';
|
|
282
|
+
token[1] = `"data-${token[1].slice(1)}"`;
|
|
283
|
+
return 1;
|
|
284
|
+
}
|
|
285
|
+
|
|
275
286
|
// ─────────────────────────────────────────────────────────────────────
|
|
276
287
|
// Hyphenated attributes
|
|
277
288
|
// data-lucide: "search" → "data-lucide": "search"
|
|
289
|
+
// $my-thing: "x" → "data-my-thing": "x"
|
|
278
290
|
// ─────────────────────────────────────────────────────────────────────
|
|
279
291
|
if (tag === 'IDENTIFIER' && !token.spaced) {
|
|
280
292
|
let parts = [token[1]];
|
|
@@ -292,8 +304,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
292
304
|
}
|
|
293
305
|
}
|
|
294
306
|
if (parts.length > 1 && j > i + 1 && tokens[j - 1][0] === 'PROPERTY') {
|
|
307
|
+
let joined = parts.join('-');
|
|
308
|
+
if (joined[0] === '$') joined = 'data-' + joined.slice(1);
|
|
295
309
|
token[0] = 'STRING';
|
|
296
|
-
token[1] = `"${
|
|
310
|
+
token[1] = `"${joined}"`;
|
|
297
311
|
tokens.splice(i + 1, j - i - 1);
|
|
298
312
|
return 1;
|
|
299
313
|
}
|
|
@@ -1385,21 +1399,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1385
1399
|
|
|
1386
1400
|
const valueCode = this.generateInComponent(value, 'value');
|
|
1387
1401
|
|
|
1388
|
-
//
|
|
1402
|
+
// value/checked with reactive deps: one-way push (use <=> for two-way)
|
|
1389
1403
|
if ((key === 'value' || key === 'checked') && this.hasReactiveDeps(value)) {
|
|
1390
1404
|
this._pushEffect(`${elVar}.${key} = ${valueCode};`);
|
|
1391
|
-
const rootMemberImplicit = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
|
|
1392
|
-
if (this.isSimpleAssignable(value) || rootMemberImplicit) {
|
|
1393
|
-
const event = key === 'checked' ? 'change' : 'input';
|
|
1394
|
-
const accessor = key === 'checked' ? 'e.target.checked'
|
|
1395
|
-
: (inputType === 'number' || inputType === 'range') ? 'e.target.valueAsNumber'
|
|
1396
|
-
: 'e.target.value';
|
|
1397
|
-
let assignCode = `${valueCode} = ${accessor}`;
|
|
1398
|
-
if (rootMemberImplicit) {
|
|
1399
|
-
assignCode += `; ${this._self}.${rootMemberImplicit}.touch?.()`;
|
|
1400
|
-
}
|
|
1401
|
-
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
|
|
1402
|
-
}
|
|
1403
1405
|
continue;
|
|
1404
1406
|
}
|
|
1405
1407
|
|
|
@@ -1641,7 +1643,15 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1641
1643
|
|
|
1642
1644
|
const varNames = Array.isArray(vars) ? vars : [vars];
|
|
1643
1645
|
const itemVar = varNames[0];
|
|
1644
|
-
|
|
1646
|
+
let indexVar = varNames[1] || null;
|
|
1647
|
+
if (!indexVar) {
|
|
1648
|
+
const usedNames = new Set(this._loopVarStack.flatMap(v => [v.itemVar, v.indexVar]));
|
|
1649
|
+
usedNames.add(itemVar);
|
|
1650
|
+
for (const candidate of ['i', 'j', 'k', 'l', 'm', 'n']) {
|
|
1651
|
+
if (!usedNames.has(candidate)) { indexVar = candidate; break; }
|
|
1652
|
+
}
|
|
1653
|
+
indexVar = indexVar || `_i${this._loopVarStack.length}`;
|
|
1654
|
+
}
|
|
1645
1655
|
|
|
1646
1656
|
const collectionCode = this.generateInComponent(collection, 'value');
|
|
1647
1657
|
|