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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.89",
3
+ "version": "3.13.91",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
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 tag = typeof head[0][1] === 'string' ? head[0][1] : head[0][1].valueOf();
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") (. div __clsx) "classes"
1142
- if (classes.length === 1 && classes[0] === '__clsx') {
1143
- return this.generateDynamicTag(tag, rest, []);
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;