rip-lang 3.13.89 → 3.13.91
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/docs/RIP-LANG.md +5 -0
- package/docs/dist/rip.js +70 -7
- package/docs/dist/rip.min.js +133 -133
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +1 -1
- package/src/browser.js +3 -0
- package/src/components.js +18 -6
- package/src/lexer.js +52 -0
package/docs/dist/rip.min.js.br
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -197,12 +197,15 @@ async function processRipScripts() {
|
|
|
197
197
|
es.addEventListener('reload', (e) => {
|
|
198
198
|
if (e.data === 'styles') {
|
|
199
199
|
const t = Date.now();
|
|
200
|
+
let refreshed = 0;
|
|
200
201
|
document.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
|
|
201
202
|
if (new URL(l.href).origin !== location.origin) return;
|
|
202
203
|
const url = new URL(l.href);
|
|
203
204
|
url.searchParams.set('_r', t);
|
|
204
205
|
l.href = url.toString();
|
|
206
|
+
refreshed++;
|
|
205
207
|
});
|
|
208
|
+
if (!refreshed) location.reload();
|
|
206
209
|
} else {
|
|
207
210
|
location.reload();
|
|
208
211
|
}
|
package/src/components.js
CHANGED
|
@@ -1129,18 +1129,29 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1129
1129
|
// Call expression: (tag.class args...) or ((tag.class) args...)
|
|
1130
1130
|
if (Array.isArray(head)) {
|
|
1131
1131
|
// Nested dynamic class call: (((. div __clsx) "classes") children)
|
|
1132
|
+
// Also handles combined: (((. (. div step-card) __clsx) "classes") children)
|
|
1132
1133
|
if (Array.isArray(head[0]) && head[0][0] === '.' &&
|
|
1133
1134
|
(head[0][2] === '__clsx' || (head[0][2] instanceof String && head[0][2].valueOf() === '__clsx'))) {
|
|
1134
|
-
const
|
|
1135
|
+
const tagExpr = head[0][1];
|
|
1135
1136
|
const classExprs = head.slice(1);
|
|
1137
|
+
if (Array.isArray(tagExpr)) {
|
|
1138
|
+
const { tag, classes, id } = this.collectTemplateClasses(tagExpr);
|
|
1139
|
+
if (tag) {
|
|
1140
|
+
const staticArgs = classes.map(c => `"${c}"`);
|
|
1141
|
+
return this.generateDynamicTag(tag, classExprs, rest, staticArgs, id);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
const tag = typeof tagExpr === 'string' ? tagExpr : tagExpr.valueOf();
|
|
1136
1145
|
return this.generateDynamicTag(tag, classExprs, rest);
|
|
1137
1146
|
}
|
|
1138
1147
|
|
|
1139
1148
|
const { tag, classes, id } = this.collectTemplateClasses(head);
|
|
1140
1149
|
if (tag && this.isHtmlTag(tag)) {
|
|
1141
|
-
// Dynamic class syntax: div.("classes")
|
|
1142
|
-
if (classes.length
|
|
1143
|
-
|
|
1150
|
+
// Dynamic class syntax: div.("classes") or div.card.("classes")
|
|
1151
|
+
if (classes.length > 0 && classes[classes.length - 1] === '__clsx') {
|
|
1152
|
+
const staticClasses = classes.slice(0, -1);
|
|
1153
|
+
const staticArgs = staticClasses.map(c => `"${c}"`);
|
|
1154
|
+
return this.generateDynamicTag(tag, rest, [], staticArgs, id);
|
|
1144
1155
|
}
|
|
1145
1156
|
return this.generateTag(tag, classes, rest, id);
|
|
1146
1157
|
}
|
|
@@ -1314,18 +1325,19 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1314
1325
|
// generateDynamicTag — tag with .() CLSX dynamic classes
|
|
1315
1326
|
// --------------------------------------------------------------------------
|
|
1316
1327
|
|
|
1317
|
-
proto.generateDynamicTag = function(tag, classExprs, children) {
|
|
1328
|
+
proto.generateDynamicTag = function(tag, classExprs, children, staticClassArgs, id) {
|
|
1318
1329
|
const elVar = this.newElementVar();
|
|
1319
1330
|
if (SVG_TAGS.has(tag) || this._svgDepth > 0) {
|
|
1320
1331
|
this._createLines.push(`${elVar} = document.createElementNS('${SVG_NS}', '${tag}');`);
|
|
1321
1332
|
} else {
|
|
1322
1333
|
this._createLines.push(`${elVar} = document.createElement('${tag}');`);
|
|
1323
1334
|
}
|
|
1335
|
+
if (id) this._createLines.push(`${elVar}.id = '${id}';`);
|
|
1324
1336
|
|
|
1325
1337
|
const autoWireClaimed = this._claimAutoWire(elVar);
|
|
1326
1338
|
|
|
1327
1339
|
// Defer className emission so class: attributes can merge with .() classes
|
|
1328
|
-
const classArgs = classExprs.map(e => this.generateInComponent(e, 'value'));
|
|
1340
|
+
const classArgs = [...(staticClassArgs || []), ...classExprs.map(e => this.generateInComponent(e, 'value'))];
|
|
1329
1341
|
const prevClassArgs = this._pendingClassArgs;
|
|
1330
1342
|
const prevClassEl = this._pendingClassEl;
|
|
1331
1343
|
this._pendingClassArgs = classArgs;
|
package/src/lexer.js
CHANGED
|
@@ -1100,11 +1100,63 @@ export class Lexer {
|
|
|
1100
1100
|
return len;
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
|
+
// --------------------------------------------------------------------------
|
|
1104
|
+
// Word Literal: %w[foo bar baz] → ["foo", "bar", "baz"]
|
|
1105
|
+
// --------------------------------------------------------------------------
|
|
1106
|
+
|
|
1107
|
+
wordLiteral() {
|
|
1108
|
+
if (this.chunk[0] !== '%' || this.chunk[1] !== 'w') return 0;
|
|
1109
|
+
let opener = this.chunk[2];
|
|
1110
|
+
if (!opener || /\s/.test(opener)) return 0;
|
|
1111
|
+
|
|
1112
|
+
let PAIRS = {'[': ']', '(': ')', '{': '}', '<': '>'};
|
|
1113
|
+
let closer = PAIRS[opener] || opener;
|
|
1114
|
+
let paired = closer !== opener;
|
|
1115
|
+
|
|
1116
|
+
// Scan for the closing delimiter, counting nested pairs
|
|
1117
|
+
let depth = 1;
|
|
1118
|
+
let i = 3;
|
|
1119
|
+
while (i < this.chunk.length && depth > 0) {
|
|
1120
|
+
let ch = this.chunk[i];
|
|
1121
|
+
if (ch === '\\') { i += 2; continue; }
|
|
1122
|
+
if (paired && ch === opener) depth++;
|
|
1123
|
+
if (ch === closer) depth--;
|
|
1124
|
+
if (depth > 0) i++;
|
|
1125
|
+
}
|
|
1126
|
+
if (depth !== 0) return 0;
|
|
1127
|
+
|
|
1128
|
+
let content = this.chunk.slice(3, i);
|
|
1129
|
+
let total = i + 1; // %w + opener + content + closer
|
|
1130
|
+
|
|
1131
|
+
// Split on whitespace, handling backslash-space escapes
|
|
1132
|
+
let words = [];
|
|
1133
|
+
if (content.trim()) {
|
|
1134
|
+
let raw = content.trim().split(/(?<!\\)\s+/);
|
|
1135
|
+
for (let w of raw) {
|
|
1136
|
+
words.push(w.replace(/\\ /g, ' '));
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Emit tokens: [ STRING , STRING , STRING ]
|
|
1141
|
+
this.emit('[', '[', {len: total});
|
|
1142
|
+
for (let j = 0; j < words.length; j++) {
|
|
1143
|
+
if (j > 0) this.emit(',', ',');
|
|
1144
|
+
this.emit('STRING', `"${words[j]}"`);
|
|
1145
|
+
}
|
|
1146
|
+
this.emit(']', ']');
|
|
1147
|
+
|
|
1148
|
+
return total;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1103
1151
|
// --------------------------------------------------------------------------
|
|
1104
1152
|
// 9. Literal Token (operators, punctuation, everything else)
|
|
1105
1153
|
// --------------------------------------------------------------------------
|
|
1106
1154
|
|
|
1107
1155
|
literalToken() {
|
|
1156
|
+
// %w word literal: %w[foo bar baz] → ["foo", "bar", "baz"]
|
|
1157
|
+
let wl = this.wordLiteral();
|
|
1158
|
+
if (wl) return wl;
|
|
1159
|
+
|
|
1108
1160
|
let match = OPERATOR_RE.exec(this.chunk);
|
|
1109
1161
|
let val = match ? match[0] : this.chunk.charAt(0);
|
|
1110
1162
|
let tag = val;
|