@ape-egg/vibe 2.1.13 → 2.1.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.14] - 2026-06-22
4
+
5
+ ### Fixed
6
+
7
+ - **Compiler 1.9.8 → 1.9.9 — a component prop referenced *bare* inside an event handler threw at fire time** (`compiler/src/parser/html.rs`, `runtime/component.js`) — prop substitution into `on*` handlers only rewrote the `$.prop` form, so a bare prop reference (`onclick="pick(item)"` where `item` is a prop) was left untouched. Because a native inline handler runs in *global* scope when it fires, that bare identifier resolved to an undefined global and threw a `ReferenceError`. Both the compiler's `substitute_props` and the runtime's `renderPropsAndSlot` now also substitute the bare prop identifier — mirroring the existing `@[...]` binding pass — so the handler receives the live prop with object identity preserved: a loop-alias path (`row.sig`) becomes `(row.sig)`, which `parse.js` rewrites to the scoped accessor, and a literal prop (`limit="5"`) inlines as the numeric `5`. The bare pass skips the `$.prop` form (lookbehind guard) so the two rewrites don't collide. Tests: `e2e-runtime/prop-in-handler.html` + `components/test-prop-handler.html`, exercised by `tests/e2e/component-props.spec.js`.
8
+
3
9
  ## [2.1.13] - 2026-06-22
4
10
 
5
11
  ### Fixed
@@ -1599,7 +1599,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1599
1599
 
1600
1600
  [[package]]
1601
1601
  name = "vibe-compiler"
1602
- version = "1.9.8"
1602
+ version = "1.9.9"
1603
1603
  dependencies = [
1604
1604
  "clap",
1605
1605
  "colored",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "vibe-compiler"
3
- version = "1.9.8"
3
+ version = "1.9.9"
4
4
  edition = "2021"
5
5
  description = "Vibe framework compiler - compiles Vibe source files into optimized output"
6
6
  authors = ["Kim Korte"]
@@ -690,7 +690,13 @@ fn substitute_props(template: &str, props: &[(String, String)]) -> String {
690
690
  .replace_all(&html, |c: &regex::Captures| {
691
691
  let ev = c.get(1).unwrap().as_str();
692
692
  let body = c.get(2).unwrap().as_str();
693
- let rewritten = substitute_state_ref(body, prop_name, &state_repl);
693
+ // First the `$.prop` form, then the bare prop identifier — mirroring
694
+ // the runtime's two-step rewrite so `onclick="pick(item)"` resolves
695
+ // the live prop instead of throwing ReferenceError in global scope.
696
+ // The bare pass skips `$.prop` (its `.`-lookbehind guard) so the two
697
+ // don't collide.
698
+ let after_state = substitute_state_ref(body, prop_name, &state_repl);
699
+ let rewritten = substitute_identifier(&after_state, prop_name, &ident_repl);
694
700
  if rewritten == body {
695
701
  c.get(0).unwrap().as_str().to_string()
696
702
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ape-egg/vibe",
3
- "version": "2.1.13",
3
+ "version": "2.1.14",
4
4
  "type": "module",
5
5
  "description": "Runtime-first reactivity with optional compiler",
6
6
  "main": "index.js",
@@ -369,7 +369,12 @@ const renderPropsAndSlot = (temp, props, slotHtml) => {
369
369
  );
370
370
  const stateRegex = new RegExp(`\\$\\.${escapeRegex(propName)}(?![a-zA-Z0-9_$])`, 'gi');
371
371
  transformedHtml = transformedHtml.replace(EVENT_ATTR_REGEX, (match, evName, body) => {
372
- const rewritten = body.replace(stateRegex, `$.${path}`);
372
+ // Substitute the bare prop identifier too (not just the `$.prop` form),
373
+ // mirroring the @[...] binding pass above — so `onclick="pick(item)"`
374
+ // resolves the live prop, not a global that throws ReferenceError. A
375
+ // loop-alias path (`row.sig`) becomes `(row.sig)` here; parse.js then
376
+ // rewrites the alias to `$scope(this,'row')`.
377
+ const rewritten = substituteInExpr(body.replace(stateRegex, `$.${path}`), `(${path})`);
373
378
  return rewritten === body ? match : `on${evName}="${rewritten}"`;
374
379
  });
375
380
  } else {
@@ -395,7 +400,9 @@ const renderPropsAndSlot = (temp, props, slotHtml) => {
395
400
  );
396
401
  const stateRegex = new RegExp(`\\$\\.${escapeRegex(propName)}(?![a-zA-Z0-9_$])`, 'gi');
397
402
  transformedHtml = transformedHtml.replace(EVENT_ATTR_REGEX, (match, evName, body) => {
398
- const rewritten = body.replace(stateRegex, `$.${literal}`);
403
+ // Same bare-identifier substitution for a literal prop: `onclick="set(limit)"`
404
+ // with `limit="5"` becomes `set(5)`.
405
+ const rewritten = substituteInExpr(body.replace(stateRegex, `$.${literal}`), literal);
399
406
  return rewritten === body ? match : `on${evName}="${rewritten}"`;
400
407
  });
401
408
  }