From c6defb4e110ff4e2f6478ba4c51998aead9c274a Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Wed, 11 Jun 2025 14:02:24 +0200 Subject: [PATCH] fix: signal attribute values not working with precompile transform --- jsx-runtime/src/index.js | 17 +++++++++++++- jsx-runtime/test/browser/jsx-runtime.test.js | 24 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/jsx-runtime/src/index.js b/jsx-runtime/src/index.js index 1b20dc2fbf..bfd17bd4fc 100644 --- a/jsx-runtime/src/index.js +++ b/jsx-runtime/src/index.js @@ -95,6 +95,19 @@ function jsxTemplate(templates, ...exprs) { const JS_TO_CSS = {}; const CSS_REGEX = /[A-Z]/g; +/** + * Unwrap potential signals. + * @param {*} value + * @returns {*} + */ +function normalizeAttrValue(value) { + return value !== null && + typeof value === 'object' && + typeof value.valueOf === 'function' + ? value.valueOf() + : value; +} + /** * Serialize an HTML attribute to a string. This function is not * expected to be used directly, but rather through a precompile @@ -109,6 +122,8 @@ function jsxAttr(name, value) { if (typeof result === 'string') return result; } + value = normalizeAttrValue(value); + if (name === 'ref' || name === 'key') return ''; if (name === 'style' && typeof value === 'object') { let str = ''; @@ -145,7 +160,7 @@ function jsxAttr(name, value) { return ''; } else if (value === true) return name; - return name + '="' + encodeEntities(value) + '"'; + return name + '="' + encodeEntities('' + value) + '"'; } /** diff --git a/jsx-runtime/test/browser/jsx-runtime.test.js b/jsx-runtime/test/browser/jsx-runtime.test.js index 8a889226ba..4bfb2f30c0 100644 --- a/jsx-runtime/test/browser/jsx-runtime.test.js +++ b/jsx-runtime/test/browser/jsx-runtime.test.js @@ -11,6 +11,24 @@ import { import { setupScratch, teardown } from '../../../test/_util/helpers'; import { encodeEntities } from '../../src/utils'; +function createSignal(value) { + return { + value, + peek() { + return value; + }, + subscribe() { + return () => {}; + }, + valueOf() { + return value; + }, + toString() { + return String(value); + } + }; +} + describe('Babel jsx/jsxDEV', () => { let scratch; let prevVNodeOption; @@ -167,6 +185,12 @@ describe('precompiled JSX', () => { ); }); + it('should support signals', () => { + const sig = createSignal(`&<'"`); + expect(jsxAttr('foo', sig)).to.equal(`foo="&<'""`); + expect(jsxAttr('style', sig)).to.equal(`style="&<'""`); + }); + it('should call options.attr()', () => { options.attr = (name, value) => { return `data-${name}="foo${value}"`;