@pushwoosh/dumb-components 1.1.38 → 1.1.40

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/dumb-components",
3
- "version": "1.1.38",
3
+ "version": "1.1.40",
4
4
  "description": "React components to build Pushwoosh products",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -37,10 +37,10 @@
37
37
  "@codemirror/lang-liquid": "^6.2.2",
38
38
  "@floating-ui/react-dom": "^2.1.2",
39
39
  "@lezer/highlight": "^1.2.1",
40
- "@pushwoosh/kit-constants": "^1.6.9",
41
- "@pushwoosh/kit-helpers": "^4.8.2",
42
- "@pushwoosh/kit-icons": "^2.1.2",
43
- "@pushwoosh/kit-typography": "^1.7.3",
40
+ "@pushwoosh/kit-constants": "^1.6.12",
41
+ "@pushwoosh/kit-helpers": "^4.8.5",
42
+ "@pushwoosh/kit-icons": "^2.1.8",
43
+ "@pushwoosh/kit-typography": "^1.7.4",
44
44
  "@tippyjs/react": "^4.2.6",
45
45
  "@uiw/codemirror-themes": "^4.23.8",
46
46
  "@uiw/react-codemirror": "^4.23.8",
@@ -1,3 +1,4 @@
1
1
  import { type ScalarValue, type ASTNodeList, type IfOperator } from './types';
2
+ export declare const voidTags: Set<string>;
2
3
  export declare const ifOperatorFns: Record<IfOperator, (a: ScalarValue, b: ScalarValue) => boolean>;
3
4
  export declare function stringToAst(template: string): ASTNodeList;
@@ -7,6 +7,8 @@ const pairedControlTokensReversed = {
7
7
  endfor: 'for',
8
8
  endif: 'if'
9
9
  };
10
+ // Add a list of void/self-closing HTML-like tags that do not require an explicit closing tag
11
+ export const voidTags = new Set(['br', 'hr', 'img']);
10
12
  function tokenizeLiquid(template) {
11
13
  const tokens = [];
12
14
  let cursor = 0;
@@ -106,25 +108,40 @@ function tokenizeLiquid(template) {
106
108
  }
107
109
  };
108
110
  const captureTag = () => {
109
- const value = captureUntil('>', 1);
110
- const [tagName, args] = smartSplitAtFirstDelimiter(value, ' ');
111
- if (tagName.startsWith('/')) {
111
+ const raw = captureUntil('>', 1);
112
+ const value = raw.trim();
113
+ // Closing tag: </tag>
114
+ if (value.startsWith('/')) {
115
+ const endName = value.slice(1).trim();
112
116
  tokens.push({
113
117
  type: 'TagEnd',
114
- value: tagName.slice(1),
118
+ value: endName,
115
119
  lineNo
116
120
  });
117
- } else {
121
+ return;
122
+ }
123
+ // Detect explicit self-closing syntax: <tag ... /> or <tag/>
124
+ const hasExplicitSelfClose = /\/$/.test(value);
125
+ const normalized = hasExplicitSelfClose ? value.replace(/\/$/, '').trimEnd() : value;
126
+ const [tagNameRaw, args] = smartSplitAtFirstDelimiter(normalized, ' ');
127
+ const tagName = tagNameRaw.trim();
128
+ tokens.push({
129
+ type: 'Tag',
130
+ value: tagName,
131
+ lineNo
132
+ });
133
+ tokens.push({
134
+ type: 'TagArg',
135
+ value: (args || '').trim(),
136
+ lineNo
137
+ });
138
+ // If the tag is a known void tag or explicitly self-closed, emit an immediate TagEnd
139
+ if (hasExplicitSelfClose || voidTags.has(tagName.toLowerCase())) {
118
140
  tokens.push({
119
- type: 'Tag',
141
+ type: 'TagEnd',
120
142
  value: tagName,
121
143
  lineNo
122
144
  });
123
- tokens.push({
124
- type: 'TagArg',
125
- value: args || '',
126
- lineNo
127
- });
128
145
  }
129
146
  };
130
147
  while (!isEOF()) {
@@ -4,7 +4,7 @@ import { Color } from '@pushwoosh/kit-constants';
4
4
  import { variants } from '@pushwoosh/kit-typography/map';
5
5
  import { filters } from './filters';
6
6
  import { stringifyScalarOrVariable, unpackScalarOrValue } from './helpers';
7
- import { ifOperatorFns } from './parser';
7
+ import { ifOperatorFns, voidTags } from './parser';
8
8
  // Applies a filter to a value.
9
9
  // @param filterName The name of the filter to apply.
10
10
  // @param value The value to which the filter should be applied.
@@ -75,7 +75,13 @@ function renderNodesToArray(ast, context, renderTag) {
75
75
  return output;
76
76
  }
77
77
  export function renderToString(ast, context) {
78
- const outArr = renderNodesToArray(ast, context, (tagNode, children) => [`<${tagNode.name} ${Object.entries(tagNode.params).map(([key, value]) => `${key}=${stringifyScalarOrVariable(value)}`).join(' ')}>`, ...children, `</${tagNode.name}>`]);
78
+ const outArr = renderNodesToArray(ast, context, (tagNode, children) => {
79
+ const attrs = Object.entries(tagNode.params).map(([key, value]) => `${key}=${stringifyScalarOrVariable(value)}`).join(' ');
80
+ if (voidTags.has(tagNode.name)) {
81
+ return [attrs ? `<${tagNode.name} ${attrs}/>` : `<${tagNode.name}/>`];
82
+ }
83
+ return [attrs ? `<${tagNode.name} ${attrs}>` : `<${tagNode.name}>`, ...children, `</${tagNode.name}>`];
84
+ });
79
85
  return outArr.join('');
80
86
  }
81
87
  function getColor(color) {
@@ -107,6 +113,12 @@ export function renderToReact(ast, context) {
107
113
  key: key
108
114
  }, children)];
109
115
  }
116
+ if (voidTags.has(TagName)) {
117
+ return [React.createElement(TagName, {
118
+ ...props,
119
+ key: key
120
+ })];
121
+ }
110
122
  return [React.createElement(TagName, {
111
123
  ...props,
112
124
  key: key