From 4cd220b3505bc2894d8c3af953a9ee141d39c01f Mon Sep 17 00:00:00 2001 From: Pawankalyan2023 Date: Sun, 12 Oct 2025 14:25:02 +0530 Subject: [PATCH 1/2] Fix:Can't bind future values outside of HTML tags --- package-lock.json | 6 +++--- src/parser/parser.test.ts | 25 +++++++++++++++++++++++++ src/parser/parser.ts | 26 ++++++++++++++++++++------ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index dffedc9..c9bd06a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rimmel", - "version": "1.4.5-rc3", + "version": "1.5.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rimmel", - "version": "1.4.5-rc3", + "version": "1.5.2", "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", @@ -30,7 +30,7 @@ "vite": "^5.4.9" }, "peerDependencies": { - "rxjs": ">=5.5.0 || >=6.0.0 || >=7.0.0 || ^8.0.0-alpha.0" + "rxjs": ">=5.5.0 || >=6.0.0 || >=7.0.0 || >=8.0.0-0 <8.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/src/parser/parser.test.ts b/src/parser/parser.test.ts index ca02078..06b5bad 100644 --- a/src/parser/parser.test.ts +++ b/src/parser/parser.test.ts @@ -2,6 +2,7 @@ import { of, BehaviorSubject, Subject } from 'rxjs'; import { state, waitingElementHandlers } from '../internal-state'; import { AttributeObjectSink } from '../sinks/attribute-sink'; import { InnerText } from '../sinks/inner-text-sink'; +import { TextContent } from '../sinks/text-content-sink'; import { RMLEventName } from '../types/dom'; import { rml } from './parser'; @@ -482,6 +483,30 @@ describe('Parser', () => { }); + describe('Plain text templates', () => { + + it('works with promises in plain text', () => { + const deferredValue = defer('world'); + const template = rml`Hello, ${deferredValue}`; + + expect(template).toMatch(/Hello, world/); + expect(waitingElementHandlers.get('RMLREF+0')).toEqual([ + { source: deferredValue, sink: TextContent, type: 'sink' }, + ]); + }); + + it('works with observables in plain text', () => { + const observableValue = of('world'); + const template = rml`Hello, ${observableValue}`; + + expect(template).toMatch(/Hello, world/); + expect(waitingElementHandlers.get('RMLREF+0')).toEqual([ + { source: observableValue, sink: TextContent, type: 'sink' }, + ]); + }); + + }); + describe('When multiple mixins are passed together', () => { it('sets them correctly when adjacent', () => { diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 05e7bb7..1bb5aad 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -281,16 +281,30 @@ export function rml(strings: TemplateStringsArray, ...expressions: RMLTemplateEx +(existingRef ? string : string.replace(/\s*>\s*$/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`)) +(initialValue ?? '') ; - + } else if(/>?\s*[^<]*$/m.test(string) && /^\s*[^<]*\s*/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`) +ref; - acc += (existingRef?string:string.replace(/\s*>(?=[^<]*$)/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`)) +INTERACTIVE_NODE_START +(initialValue ?? '') +INTERACTIVE_NODE_END; + // If there is no tag '>' to attach the resolve attribute, + // inject a tiny placeholder element that carries the RESOLVE_ATTRIBUTE so the sink + // can later find and bind to a DOM node. + const hasClosingTagChar = />\s*[^<]*$/.test(string); + let renderedPrefix: string; + if (existingRef) { + // An element with an existing ref already; just reuse the string as-is + renderedPrefix = string; + } else if (hasClosingTagChar && /\s*>(?=[^<]*$)/.test(string)) { + // Normal case: inject the resolve attribute into the nearest closing tag area + renderedPrefix = string.replace(/\s*>(?=[^<]*$)/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`); + } else { + // Emit a zero-width placeholder element which will receive the sink. + // Keep the placeholder empty so it doesn't affect layout. + renderedPrefix = `${string}`; + } + + // Append the interactive markers and initial value + acc += renderedPrefix + INTERACTIVE_NODE_START + (initialValue ?? '') + INTERACTIVE_NODE_END; } else { acc = accPlusString; From 7cdc84740a62b2f6b1cf2a1d229cea5c00008778 Mon Sep 17 00:00:00 2001 From: Pawankalyan2023 Date: Sun, 12 Oct 2025 14:30:57 +0530 Subject: [PATCH 2/2] Fix:Can't bind future values outside of HTML tags:changes --- src/parser/parser.test.ts | 27 +-------------------------- src/parser/parser.ts | 31 ++++++------------------------- 2 files changed, 7 insertions(+), 51 deletions(-) diff --git a/src/parser/parser.test.ts b/src/parser/parser.test.ts index 06b5bad..b25bc7f 100644 --- a/src/parser/parser.test.ts +++ b/src/parser/parser.test.ts @@ -2,7 +2,6 @@ import { of, BehaviorSubject, Subject } from 'rxjs'; import { state, waitingElementHandlers } from '../internal-state'; import { AttributeObjectSink } from '../sinks/attribute-sink'; import { InnerText } from '../sinks/inner-text-sink'; -import { TextContent } from '../sinks/text-content-sink'; import { RMLEventName } from '../types/dom'; import { rml } from './parser'; @@ -483,30 +482,6 @@ describe('Parser', () => { }); - describe('Plain text templates', () => { - - it('works with promises in plain text', () => { - const deferredValue = defer('world'); - const template = rml`Hello, ${deferredValue}`; - - expect(template).toMatch(/Hello, world/); - expect(waitingElementHandlers.get('RMLREF+0')).toEqual([ - { source: deferredValue, sink: TextContent, type: 'sink' }, - ]); - }); - - it('works with observables in plain text', () => { - const observableValue = of('world'); - const template = rml`Hello, ${observableValue}`; - - expect(template).toMatch(/Hello, world/); - expect(waitingElementHandlers.get('RMLREF+0')).toEqual([ - { source: observableValue, sink: TextContent, type: 'sink' }, - ]); - }); - - }); - describe('When multiple mixins are passed together', () => { it('sets them correctly when adjacent', () => { @@ -627,4 +602,4 @@ describe('Parser', () => { describe('Plain Objects', () => { }); -}); +}); \ No newline at end of file diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 1bb5aad..cde8775 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -281,33 +281,14 @@ export function rml(strings: TemplateStringsArray, ...expressions: RMLTemplateEx +(existingRef ? string : string.replace(/\s*>\s*$/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`)) +(initialValue ?? '') ; - + } else if(/>?\s*[^<]*$/m.test(string) && /^\s*[^<]*\s*' to attach the resolve attribute, - // inject a tiny placeholder element that carries the RESOLVE_ATTRIBUTE so the sink - // can later find and bind to a DOM node. - const hasClosingTagChar = />\s*[^<]*$/.test(string); - let renderedPrefix: string; - if (existingRef) { - // An element with an existing ref already; just reuse the string as-is - renderedPrefix = string; - } else if (hasClosingTagChar && /\s*>(?=[^<]*$)/.test(string)) { - // Normal case: inject the resolve attribute into the nearest closing tag area - renderedPrefix = string.replace(/\s*>(?=[^<]*$)/, ` ${RESOLVE_ATTRIBUTE}="${ref}">`); - } else { - // Emit a zero-width placeholder element which will receive the sink. - // Keep the placeholder empty so it doesn't affect layout. - renderedPrefix = `${string}`; - } - - // Append the interactive markers and initial value - acc += renderedPrefix + INTERACTIVE_NODE_START + (initialValue ?? '') + INTERACTIVE_NODE_END; - + acc += string + `` + (initialValue ?? '') + ''; } else { - acc = accPlusString; + // Handle non-future expressions in plain text or other contexts + acc = accPlusString + (expression ?? ''); } } @@ -316,4 +297,4 @@ export function rml(strings: TemplateStringsArray, ...expressions: RMLTemplateEx acc += strings[strlen]; return acc; -} +} \ No newline at end of file