@burger-editor/frozen-patty 0.11.0 → 4.0.0-alpha.10

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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * フィールド名と属性名からフィールド定義文字列を生成します
3
+ * @param fieldName フィールド名
4
+ * @param propName 属性名(オプション)
5
+ * @returns フィールド定義文字列
6
+ */
7
+ export function fieldNameStringify(fieldName, propName) {
8
+ if (!propName) {
9
+ return fieldName;
10
+ }
11
+ // fieldNameとpropNameが同じ場合は省略記法を使用
12
+ if (fieldName === propName) {
13
+ return `:${propName}`;
14
+ }
15
+ return `${fieldName}:${propName}`;
16
+ }
17
+ /**
18
+ * 複数のフィールド定義をカンマ区切りで結合します
19
+ * @param fields フィールド定義の配列
20
+ * @returns カンマ区切りのフィールド定義文字列
21
+ */
22
+ export function fieldsStringify(fields) {
23
+ return fields
24
+ .map((field) => fieldNameStringify(field.fieldName, field.propName))
25
+ .join(', ');
26
+ }
@@ -15,7 +15,6 @@ export default class FrozenPatty {
15
15
  export interface FrozenPattyOptions {
16
16
  /**
17
17
  * **Data attribute** name for specifying the node that FrozenPatty treats as a _field_
18
- *
19
18
  * @default `"field"`
20
19
  */
21
20
  attr?: string;
@@ -28,7 +27,6 @@ export interface FrozenPattyOptions {
28
27
  * - `"1"` => `1`
29
28
  * - `"1.0"` => `1`
30
29
  * - `"0.1"` => `0.1`
31
- *
32
30
  * @default `false`
33
31
  */
34
32
  typeConvert?: boolean;
@@ -36,4 +34,11 @@ export interface FrozenPattyOptions {
36
34
  * Value filter
37
35
  */
38
36
  valueFilter?: Filter;
37
+ /**
38
+ * Enable XSS protection
39
+ * - When enabled, removes potentially malicious code that may be contained in HTML
40
+ * - Prevents script elements, dangerous event handler attributes, javascript protocol, etc.
41
+ * @default `true`
42
+ */
43
+ xssSanitize?: boolean;
39
44
  }
@@ -1,5 +1,6 @@
1
- import get from './get.js';
2
- import set from './set.js';
1
+ import { getComponent } from './get-component.js';
2
+ import { setComponent } from './set-component.js';
3
+ import { sanitizeHtml } from './utils.js';
3
4
  export default class FrozenPatty {
4
5
  #attr = 'field';
5
6
  #dom;
@@ -8,6 +9,10 @@ export default class FrozenPatty {
8
9
  */
9
10
  #filter;
10
11
  #typeConvert = false;
12
+ /**
13
+ * Enable XSS protection
14
+ */
15
+ #xssSanitize = true;
11
16
  /**
12
17
  *
13
18
  * @param html Original HTML
@@ -15,7 +20,14 @@ export default class FrozenPatty {
15
20
  */
16
21
  constructor(html, options) {
17
22
  this.#dom = document.createElement('fp-placeholer');
18
- this.#dom.innerHTML = html;
23
+ // Sanitize initial HTML if XSS protection is enabled
24
+ if (options?.xssSanitize === false) {
25
+ this.#dom.innerHTML = html;
26
+ this.#xssSanitize = false;
27
+ }
28
+ else {
29
+ this.#dom.innerHTML = sanitizeHtml(html);
30
+ }
19
31
  if (options) {
20
32
  if (options.attr) {
21
33
  this.#attr = options.attr;
@@ -27,7 +39,8 @@ export default class FrozenPatty {
27
39
  merge(data) {
28
40
  const currentData = this.toJSON(false);
29
41
  const newData = Object.assign(currentData, data);
30
- this.#dom = set(this.#dom, newData, this.#attr, this.#filter);
42
+ // Pass XSS protection flag to set (default is true if not set)
43
+ this.#dom = setComponent(this.#dom, newData, this.#attr, this.#filter, this.#xssSanitize);
31
44
  return this;
32
45
  }
33
46
  toDOM() {
@@ -38,6 +51,6 @@ export default class FrozenPatty {
38
51
  }
39
52
  toJSON(filtering = true) {
40
53
  const filter = filtering ? this.#filter : undefined;
41
- return get(this.#dom, this.#attr, this.#typeConvert, filter);
54
+ return getComponent(this.#dom, this.#attr, this.#typeConvert, filter);
42
55
  }
43
56
  }
@@ -0,0 +1,9 @@
1
+ import type { Filter, FrozenPattyData } from './types.js';
2
+ /**
3
+ *
4
+ * @param el
5
+ * @param attr
6
+ * @param typeConvert
7
+ * @param filter
8
+ */
9
+ export declare function getComponent(el: Element, attr: string, typeConvert: boolean, filter?: Filter): FrozenPattyData;
@@ -0,0 +1,29 @@
1
+ import { getValues } from './get-values.js';
2
+ import { arrayToHash } from './utils.js';
3
+ const reverseListElementSelectors = ['picture'];
4
+ /**
5
+ *
6
+ * @param el
7
+ * @param attr
8
+ * @param typeConvert
9
+ * @param filter
10
+ */
11
+ export function getComponent(el, attr, typeConvert, filter) {
12
+ el = el.cloneNode(true);
13
+ const reverseListElements = el.querySelectorAll(`:is(${reverseListElementSelectors.join(',')})[data-${attr}-list]`);
14
+ for (const reverseListElement of reverseListElements) {
15
+ const children = [];
16
+ while (reverseListElement.lastElementChild) {
17
+ children.push(reverseListElement.lastElementChild);
18
+ reverseListElement.lastElementChild.remove();
19
+ }
20
+ reverseListElement.append(...children);
21
+ }
22
+ const filedElements = el.querySelectorAll(`[data-${attr}]`);
23
+ let values = [];
24
+ for (const _el of filedElements) {
25
+ values = [...values, ...getValues(_el, typeConvert, attr, filter)];
26
+ }
27
+ const result = arrayToHash(values);
28
+ return result;
29
+ }
@@ -1,10 +1,10 @@
1
1
  import type { Filter, PrimitiveDatum } from './types.js';
2
2
  /**
3
3
  * Get value from an element
4
- *
5
4
  * @param el A target element
6
5
  * @param name A label name
7
6
  * @param typeConvert Auto covert type of value
8
7
  * @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
8
+ * @param filter
9
9
  */
10
10
  export declare function getValue(el: Element, name: string, typeConvert?: boolean, attr?: string, filter?: Filter): PrimitiveDatum | PrimitiveDatum[];
package/dist/get-value.js CHANGED
@@ -2,11 +2,11 @@ import { getValues } from './get-values.js';
2
2
  import { arrayToHash } from './utils.js';
3
3
  /**
4
4
  * Get value from an element
5
- *
6
5
  * @param el A target element
7
6
  * @param name A label name
8
7
  * @param typeConvert Auto covert type of value
9
8
  * @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
9
+ * @param filter
10
10
  */
11
11
  export function getValue(el, name, typeConvert = false, attr = 'field', filter) {
12
12
  const value = getValues(el, typeConvert, attr, filter);
@@ -1,10 +1,9 @@
1
1
  import type { Filter, PrimitiveDatum } from './types.js';
2
2
  /**
3
3
  * Get value from an element
4
- *
5
4
  * @param el A target element
6
- * @param name A label name
7
- * @param typeConvert Auto covert type of value
5
+ * @param convertType Auto covert type of value
8
6
  * @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
7
+ * @param filter
9
8
  */
10
- export declare function getValues(el: Element, typeConvert?: boolean, attr?: string, filter?: Filter): [string | number, PrimitiveDatum, boolean][];
9
+ export declare function getValues(el: Element, convertType?: boolean, attr?: string, filter?: Filter): [key: string | number, value: PrimitiveDatum, forceArray: boolean][];
@@ -1,81 +1,93 @@
1
+ import { kebabCase } from '@burger-editor/utils';
2
+ import { parseFields } from './parse-fields.js';
1
3
  /**
2
4
  * Get value from an element
3
- *
4
5
  * @param el A target element
5
- * @param name A label name
6
- * @param typeConvert Auto covert type of value
6
+ * @param convertType Auto covert type of value
7
7
  * @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
8
+ * @param filter
8
9
  */
9
- export function getValues(el, typeConvert = false, attr = 'field', filter) {
10
- /**
11
- * [key, value, forceArray]
12
- */
10
+ export function getValues(el, convertType = false, attr = 'field', filter) {
13
11
  const result = [];
14
- const rawValue = el.getAttribute(`data-${attr}`);
15
- const listRoot = el.closest(`[data-${attr}-list]`);
16
- const forceArray = !!listRoot;
17
- if (rawValue == null) {
12
+ const query = el.getAttribute(`data-${attr}`);
13
+ if (query == null) {
18
14
  throw new Error(`data-${attr} attriblute is empty.`);
19
15
  }
20
- const fieldList = `${rawValue}`.split(/\s*,\s*/);
21
- // console.log({fieldList, el: el.innerHTML});
22
- for (let fieldName of fieldList) {
23
- let splitKey;
24
- let keyAttr = '';
16
+ const listRoot = el.closest(`[data-${attr}-list]`);
17
+ const forceArray = !!listRoot;
18
+ const fields = parseFields(query);
19
+ for (const field of fields) {
25
20
  let value;
26
- fieldName = fieldName.trim();
27
- if (/^[_a-z-][\w-]*:[_a-z-][\w-]*(?:\([a-z-]+\))?/i.test(fieldName)) {
28
- splitKey = fieldName.split(':');
29
- fieldName = splitKey[0]?.trim() ?? '';
30
- keyAttr = splitKey[1]?.trim() ?? '';
31
- }
32
- // console.log({fieldName, keyAttr, el: el.innerHTML});
33
- if (keyAttr === 'text') {
34
- value = el.innerHTML;
35
- }
36
- else if (/^style\([a-z-]+\)$/i.test(keyAttr)) {
37
- const css = keyAttr.replace(/^style\(([a-z-]+)\)$/i, '$1');
38
- let style;
39
- if (el instanceof HTMLElement) {
40
- style = el.style;
41
- }
42
- else {
43
- style = window.getComputedStyle(el);
44
- }
45
- value = style.getPropertyValue(css);
46
- if (css === 'background-image') {
47
- value = getBackgroundImagePath(value);
21
+ const { fieldName, propName } = field;
22
+ if (propName) {
23
+ switch (propName) {
24
+ case 'node': {
25
+ value = el.localName ?? el.nodeName.toLowerCase();
26
+ break;
27
+ }
28
+ case 'text': {
29
+ value = el.textContent?.trim() ?? '';
30
+ break;
31
+ }
32
+ case 'html': {
33
+ value = el.innerHTML.trim();
34
+ break;
35
+ }
36
+ case 'style': {
37
+ if (el instanceof HTMLElement) {
38
+ value = el.style.cssText;
39
+ }
40
+ break;
41
+ }
42
+ default: {
43
+ if (/^style\([a-z-]+\)$/i.test(propName)) {
44
+ const css = propName.replace(/^style\(([a-z-]+)\)$/i, '$1');
45
+ let style;
46
+ if (el instanceof HTMLElement) {
47
+ style = el.style;
48
+ }
49
+ else {
50
+ style = window.getComputedStyle(el);
51
+ }
52
+ value = style.getPropertyValue(css);
53
+ if (css === 'background-image') {
54
+ value = getBackgroundImagePath(value);
55
+ }
56
+ break;
57
+ }
58
+ value = getAttribute(el, propName, attr, convertType);
59
+ }
48
60
  }
49
61
  }
50
- else if (keyAttr) {
51
- value = getAttribute(el, keyAttr, typeConvert);
52
- }
53
62
  else {
54
63
  if (el instanceof HTMLInputElement ||
55
64
  el instanceof HTMLSelectElement ||
56
65
  el instanceof HTMLTextAreaElement) {
57
66
  const val = el.value;
58
- if (Array.isArray(val)) {
59
- value = cast(val[0]);
60
- }
61
- else {
62
- value = cast(val);
63
- }
67
+ value = convertType ? convert(val) : val;
64
68
  }
65
69
  else {
66
- value = el.innerHTML;
70
+ value = el.innerHTML.trim();
67
71
  }
68
72
  }
69
- // console.log({fieldName, value});
70
73
  if (filter) {
71
74
  value = filter(value);
72
75
  }
76
+ if (value === undefined) {
77
+ continue;
78
+ }
73
79
  result.push([fieldName, value, forceArray]);
74
80
  }
75
- // console.log({result});
76
81
  return result;
77
82
  }
78
- function getAttribute(el, keyAttr, typeConvert) {
83
+ /**
84
+ *
85
+ * @param el
86
+ * @param keyAttr
87
+ * @param attr
88
+ * @param typeConvert
89
+ */
90
+ function getAttribute(el, keyAttr, attr, typeConvert) {
79
91
  switch (keyAttr) {
80
92
  case 'contenteditable': {
81
93
  if (el instanceof HTMLElement) {
@@ -96,43 +108,68 @@ function getAttribute(el, keyAttr, typeConvert) {
96
108
  // So to get inactive it is necessary to use the "hasAttribute" method.
97
109
  return el.hasAttribute('download') ? el.getAttribute('download') : null;
98
110
  }
99
- case 'href': {
100
- // return (el as HTMLAnchorElement).href;
101
- return el.getAttribute(keyAttr) || ''; // return plain string
111
+ case 'href':
112
+ case 'src': {
113
+ // Example: (el as HTMLAnchorElement).href;
114
+ // Expected: return defined value of plain string
115
+ return el.getAttribute(keyAttr) ?? '';
102
116
  }
103
- default: {
104
- if (keyAttr.startsWith('data-')) {
105
- const value = el.getAttribute(keyAttr) || '';
106
- if (typeConvert) {
107
- return cast(value);
108
- }
109
- return value;
110
- }
111
- return el.getAttribute(keyAttr) || '';
117
+ }
118
+ let value =
119
+ // @ts-ignore
120
+ el[keyAttr];
121
+ if (value !== undefined) {
122
+ if (typeConvert) {
123
+ value = convert(value);
112
124
  }
125
+ return value;
113
126
  }
114
- }
115
- function cast(value) {
116
- if (value == null || value === '') {
127
+ // For shorthand notation, get value from data-field-* attribute
128
+ const dataAttr = ['data', attr, kebabCase(keyAttr)].join('-');
129
+ if (el.hasAttribute(dataAttr)) {
130
+ return el.getAttribute(dataAttr) ?? '';
131
+ }
132
+ value = el.getAttribute(keyAttr) ?? null;
133
+ if ((value === '' || value == null) &&
134
+ typeof keyAttr === 'string' &&
135
+ keyAttr.startsWith('data-')) {
117
136
  return '';
118
137
  }
119
- switch (value) {
120
- case 'true': {
121
- return true;
122
- }
123
- case 'false': {
124
- return false;
125
- }
138
+ if (typeConvert) {
139
+ value = convert(value);
126
140
  }
127
- const numeric = Number(value);
128
- if (Number.isFinite(numeric)) {
129
- return numeric;
141
+ return value;
142
+ }
143
+ /**
144
+ *
145
+ * @param value
146
+ */
147
+ function convert(value) {
148
+ value = parse(value);
149
+ const str = `${value}`;
150
+ if (URL.canParse(str)) {
151
+ const url = new URL(str, location.href);
152
+ if (url.origin === location.origin) {
153
+ return url.pathname;
154
+ }
130
155
  }
131
156
  return value;
132
157
  }
133
158
  /**
134
- * Get path from value of "background-image"
135
159
  *
160
+ * @param value
161
+ */
162
+ function parse(value) {
163
+ try {
164
+ return JSON.parse(`${value}`);
165
+ }
166
+ catch {
167
+ return value;
168
+ }
169
+ }
170
+ /**
171
+ * Get path from value of "background-image"
172
+ * @param value
136
173
  */
137
174
  function getBackgroundImagePath(value) {
138
175
  const origin = `${location.protocol}//${location.hostname}${location.port ? `:${location.port}` : ''}`;
package/dist/get.d.ts CHANGED
@@ -1,2 +1,9 @@
1
1
  import type { Filter, FrozenPattyData } from './types.js';
2
+ /**
3
+ *
4
+ * @param el
5
+ * @param attr
6
+ * @param typeConvert
7
+ * @param filter
8
+ */
2
9
  export default function (el: Element, attr: string, typeConvert: boolean, filter?: Filter): FrozenPattyData;
package/dist/get.js CHANGED
@@ -1,5 +1,12 @@
1
1
  import { getValues } from './get-values.js';
2
2
  import { arrayToHash } from './utils.js';
3
+ /**
4
+ *
5
+ * @param el
6
+ * @param attr
7
+ * @param typeConvert
8
+ * @param filter
9
+ */
3
10
  export default function (el, attr, typeConvert, filter) {
4
11
  // eslint-disable-next-line unicorn/prefer-spread
5
12
  const filedElements = Array.from(el.querySelectorAll(`[data-${attr}]`));
@@ -0,0 +1,6 @@
1
+ /**
2
+ * picture要素の特別な処理を行います
3
+ * @param pictureElement picture要素
4
+ * @param attr
5
+ */
6
+ export declare function handlePictureElement(pictureElement: HTMLPictureElement, attr: string): void;
@@ -0,0 +1,31 @@
1
+ import { parseFields } from './parse-fields.js';
2
+ import { setValue } from './set-value.js';
3
+ /**
4
+ * picture要素の特別な処理を行います
5
+ * @param pictureElement picture要素
6
+ * @param attr
7
+ */
8
+ export function handlePictureElement(pictureElement, attr) {
9
+ const children = [...pictureElement.children];
10
+ const fieldAttrName = `data-${attr}`;
11
+ for (const [i, child] of children.entries()) {
12
+ const fields = parseFields(child.getAttribute(fieldAttrName) ?? '');
13
+ const newChild = child.cloneNode(false);
14
+ child.remove();
15
+ if (i > 0) {
16
+ setValue(newChild, 'node', 'source', attr);
17
+ }
18
+ switch (newChild.localName) {
19
+ case 'source': {
20
+ // newChild.removeAttribute('alt');
21
+ // setValue(newChild, name, attr.value);
22
+ break;
23
+ }
24
+ case 'img': {
25
+ //
26
+ break;
27
+ }
28
+ }
29
+ pictureElement.insertBefore(newChild, pictureElement.firstChild);
30
+ }
31
+ }
package/dist/index.d.ts CHANGED
@@ -2,8 +2,12 @@ import type { FrozenPattyOptions } from './frozen-patty.js';
2
2
  import FrozenPatty from './frozen-patty.js';
3
3
  /**
4
4
  * Pure HTML to JSON converter that not use template engine.
5
- *
6
5
  * @param html Original HTML
7
6
  * @param options Options
8
7
  */
8
+ /**
9
+ *
10
+ * @param html
11
+ * @param options
12
+ */
9
13
  export default function frozenPatty(html: string, options?: FrozenPattyOptions): FrozenPatty;
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import FrozenPatty from './frozen-patty.js';
2
2
  /**
3
3
  * Pure HTML to JSON converter that not use template engine.
4
- *
5
4
  * @param html Original HTML
6
5
  * @param options Options
7
6
  */
7
+ /**
8
+ *
9
+ * @param html
10
+ * @param options
11
+ */
8
12
  export default function frozenPatty(html, options) {
9
13
  return new FrozenPatty(html, options);
10
14
  }
@@ -0,0 +1,6 @@
1
+ import type { FieldDefinition } from './types.js';
2
+ /**
3
+ *
4
+ * @param query
5
+ */
6
+ export declare function parseFields(query: string): readonly FieldDefinition[];
@@ -0,0 +1,38 @@
1
+ import { camelCase } from '@burger-editor/utils';
2
+ const cache = new Map();
3
+ /**
4
+ *
5
+ * @param query
6
+ */
7
+ export function parseFields(query) {
8
+ const cached = cache.get(query);
9
+ if (cached) {
10
+ return cached;
11
+ }
12
+ const fields = query.split(',');
13
+ const result = fields.map(parseField);
14
+ cache.set(query, result);
15
+ return result;
16
+ }
17
+ /**
18
+ *
19
+ * @param field
20
+ */
21
+ function parseField(field) {
22
+ field = field.trim().toLowerCase();
23
+ if (!field) {
24
+ throw new Error('Field name is empty.');
25
+ }
26
+ let [fieldName, propName,
27
+ // eslint-disable-next-line prefer-const
28
+ ...rest] = field.split(':');
29
+ if (rest.length > 0) {
30
+ throw new Error('Invalid field format.');
31
+ }
32
+ propName = propName?.trim() || '';
33
+ fieldName = fieldName?.trim() || propName || field;
34
+ return {
35
+ fieldName: camelCase(fieldName),
36
+ propName,
37
+ };
38
+ }
@@ -0,0 +1,10 @@
1
+ import type { Filter, FrozenPattyData } from './types.js';
2
+ /**
3
+ *
4
+ * @param el
5
+ * @param data
6
+ * @param attr
7
+ * @param filter
8
+ * @param xssSanitize Enable XSS protection
9
+ */
10
+ export declare function setComponent(el: Element, data: FrozenPattyData, attr: string, filter?: Filter, xssSanitize?: boolean): Element;
@@ -0,0 +1,71 @@
1
+ import { setValue } from './set-value.js';
2
+ import { stringifyFields } from './stringify-fields.js';
3
+ import { definedFields, flattenData, getFields, hasField, kebabCase, maxLengthOf, removeProp, replaceNode, replaceProp, } from './utils.js';
4
+ /**
5
+ *
6
+ * @param el
7
+ * @param data
8
+ * @param attr
9
+ * @param filter
10
+ * @param xssSanitize Enable XSS protection
11
+ */
12
+ export function setComponent(el, data, attr, filter, xssSanitize = true) {
13
+ el = el.cloneNode(true);
14
+ for (const dataKeyName in data) {
15
+ const datum = data[dataKeyName];
16
+ if (Array.isArray(datum)) {
17
+ continue;
18
+ }
19
+ const targetList = [
20
+ // 自身を含める
21
+ el,
22
+ // パフォーマンスのために雑なフィルタリングを行う
23
+ ...el.querySelectorAll(`[data-${attr}*="${kebabCase(dataKeyName)}"]`),
24
+ ]
25
+ // より正確なフィルタリングを行う
26
+ .filter((el) => hasField(el, attr, dataKeyName));
27
+ for (const targetEl of targetList) {
28
+ setValue(targetEl, dataKeyName, datum, attr, filter, xssSanitize);
29
+ }
30
+ }
31
+ for (const listRoot of el.querySelectorAll(`[data-${attr}-list]`)) {
32
+ const decendants = listRoot.querySelectorAll('*');
33
+ const definedFieldsOfDecendants = new Set([...decendants].flatMap((el) => definedFields(el, attr)));
34
+ const maxLength = maxLengthOf(data, [...definedFieldsOfDecendants]);
35
+ const listItem = listRoot.children[0]?.cloneNode(true);
36
+ if (!listItem) {
37
+ continue;
38
+ }
39
+ while (listRoot.firstChild) {
40
+ listRoot.firstChild.remove();
41
+ }
42
+ for (let i = 0; i < maxLength; i++) {
43
+ let item = listItem.cloneNode(true);
44
+ const itemData = flattenData(data, i);
45
+ let reverse = false;
46
+ switch (listRoot.localName) {
47
+ case 'picture': {
48
+ reverse = true;
49
+ if (i > 0) {
50
+ // 2つ目以降はsource要素に変換
51
+ let fields = getFields(item, attr);
52
+ item = replaceNode(item, 'source', attr, xssSanitize);
53
+ fields = replaceProp(fields, 'src', 'srcset');
54
+ fields = removeProp(fields, 'alt');
55
+ const fieldQuery = stringifyFields(fields);
56
+ item.setAttribute(`data-${attr}`, fieldQuery);
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ const newEl = setComponent(item, itemData, attr, filter, xssSanitize);
62
+ if (reverse) {
63
+ listRoot.prepend(newEl);
64
+ }
65
+ else {
66
+ listRoot.append(newEl);
67
+ }
68
+ }
69
+ }
70
+ return el;
71
+ }
@@ -0,0 +1,10 @@
1
+ import type { Filter, FrozenPattyData } from './types.js';
2
+ /**
3
+ *
4
+ * @param el
5
+ * @param data
6
+ * @param attr
7
+ * @param filter
8
+ * @param xssSanitize Enable XSS protection
9
+ */
10
+ export declare function setComponent(el: Element, data: FrozenPattyData, attr: string, filter?: Filter, xssSanitize?: boolean): Element;