@oscarpalmer/toretto 0.27.0 → 0.29.0

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/src/html.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
- import {
3
- getSanitizeOptions,
4
- type SanitizeOptions,
5
- sanitizeNodes,
6
- } from './internal/sanitize';
2
+ import {sanitizeNodes} from './internal/sanitize';
7
3
 
8
4
  //
9
5
 
@@ -41,72 +37,72 @@ type HtmlOptions = {
41
37
  * Ignore caching the template element for the HTML string? _(defaults to `false`)_
42
38
  */
43
39
  ignoreCache?: boolean;
44
- } & SanitizeOptions;
40
+ };
45
41
 
46
42
  type Options = Required<HtmlOptions>;
47
43
 
48
44
  //
49
45
 
50
- function createTemplate(html: string, ignore: boolean): HTMLTemplateElement {
51
- const template = document.createElement('template');
46
+ function createHtml(value: string | HTMLTemplateElement): string {
47
+ const html = getParser().parseFromString(
48
+ typeof value === 'string' ? value : value.innerHTML,
49
+ HTML_PARSE_TYPE,
50
+ );
52
51
 
53
- template.innerHTML = html;
52
+ html.body.normalize();
54
53
 
55
- if (!ignore) {
56
- templates[html] = template;
57
- }
54
+ sanitizeNodes([html.body]);
58
55
 
59
- return template;
56
+ return html.body.innerHTML;
60
57
  }
61
58
 
62
- function getHtml(
59
+ function createTemplate(
63
60
  value: string | HTMLTemplateElement,
64
61
  options: Options,
65
- ): Node[] {
66
- if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
67
- return [];
68
- }
62
+ ): HTMLTemplateElement {
63
+ const template = document.createElement(TEMPLATE_TAG);
69
64
 
70
- const template =
71
- value instanceof HTMLTemplateElement
72
- ? value
73
- : getTemplate(value, options.ignoreCache);
65
+ template.innerHTML = createHtml(value);
74
66
 
75
- if (template == null) {
76
- return [];
67
+ if (typeof value === 'string' && !options.ignoreCache) {
68
+ templates[value] = template;
77
69
  }
78
70
 
79
- const cloned = template.content.cloneNode(true) as DocumentFragment;
80
-
81
- const scripts = cloned.querySelectorAll('script');
71
+ return template;
72
+ }
82
73
 
83
- for (const script of scripts) {
84
- script.remove();
74
+ function getNodes(value: string | HTMLTemplateElement, options: Options): Node[] {
75
+ if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
76
+ return [];
85
77
  }
86
78
 
87
- cloned.normalize();
79
+ const template = getTemplate(value, options);
88
80
 
89
- return sanitizeNodes([...cloned.childNodes], options);
81
+ return template == null ? [] : [...template.content.cloneNode(true).childNodes];
90
82
  }
91
83
 
92
84
  function getOptions(input?: HtmlOptions): Options {
93
85
  const options = isPlainObject(input) ? input : {};
94
86
 
95
- options.ignoreCache =
96
- typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
97
-
98
- options.sanitizeBooleanAttributes =
99
- typeof options.sanitizeBooleanAttributes === 'boolean'
100
- ? options.sanitizeBooleanAttributes
101
- : true;
87
+ options.ignoreCache = typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
102
88
 
103
89
  return options as Options;
104
90
  }
105
91
 
92
+ function getParser(): DOMParser {
93
+ parser ??= new DOMParser();
94
+
95
+ return parser;
96
+ }
97
+
106
98
  function getTemplate(
107
- value: string,
108
- ignore: boolean,
99
+ value: string | HTMLTemplateElement,
100
+ options: Options,
109
101
  ): HTMLTemplateElement | undefined {
102
+ if (value instanceof HTMLTemplateElement) {
103
+ return createTemplate(value, options);
104
+ }
105
+
110
106
  if (typeof value !== 'string' || value.trim().length === 0) {
111
107
  return;
112
108
  }
@@ -117,23 +113,15 @@ function getTemplate(
117
113
  return template;
118
114
  }
119
115
 
120
- const element = EXPRESSION_ID.test(value)
121
- ? document.querySelector(`#${value}`)
122
- : null;
116
+ const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
123
117
 
124
- template =
125
- element instanceof HTMLTemplateElement
126
- ? element
127
- : createTemplate(value, ignore);
118
+ template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
128
119
 
129
120
  return template;
130
121
  }
131
122
 
132
- const html = ((
133
- value: string | HTMLTemplateElement,
134
- options?: Options,
135
- ): Node[] => {
136
- return getHtml(value, getOptions(options));
123
+ const html = ((value: string | HTMLTemplateElement, options?: Options): Node[] => {
124
+ return getNodes(value, getOptions(options));
137
125
  }) as Html;
138
126
 
139
127
  html.clear = (): void => {
@@ -167,20 +155,20 @@ html.remove = (template: string): void => {
167
155
  * @param options Sanitization options
168
156
  * @returns Sanitized nodes
169
157
  */
170
- export function sanitize(
171
- value: Node | Node[],
172
- options?: SanitizeOptions,
173
- ): Node[] {
174
- return sanitizeNodes(
175
- Array.isArray(value) ? value : [value],
176
- getSanitizeOptions(options),
177
- );
158
+ export function sanitize(value: Node | Node[]): Node[] {
159
+ return sanitizeNodes(Array.isArray(value) ? value : [value]);
178
160
  }
179
161
 
180
162
  //
181
163
 
182
164
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
183
165
 
166
+ const HTML_PARSE_TYPE = 'text/html';
167
+
168
+ const TEMPLATE_TAG = 'template';
169
+
170
+ let parser: DOMParser;
171
+
184
172
  let templates: Record<string, HTMLTemplateElement> = {};
185
173
 
186
174
  //
@@ -28,10 +28,7 @@ export function isBadAttribute(attribute: Attr | Attribute): boolean;
28
28
  */
29
29
  export function isBadAttribute(name: string, value: string): boolean;
30
30
 
31
- export function isBadAttribute(
32
- first: string | Attr | Attribute,
33
- second?: string,
34
- ): boolean {
31
+ export function isBadAttribute(first: string | Attr | Attribute, second?: string): boolean {
35
32
  return isValidAttribute(
36
33
  attribute =>
37
34
  attribute == null ||
@@ -59,9 +56,7 @@ export function isBooleanAttribute(name: string): boolean;
59
56
 
60
57
  export function isBooleanAttribute(value: string | Attr | Attribute): boolean {
61
58
  return isValidAttribute(
62
- attribute =>
63
- attribute != null &&
64
- booleanAttributes.includes(attribute.name.toLowerCase()),
59
+ attribute => attribute != null && booleanAttributes.includes(attribute.name.toLowerCase()),
65
60
  value,
66
61
  '',
67
62
  );
@@ -72,9 +67,7 @@ export function isBooleanAttribute(value: string | Attr | Attribute): boolean {
72
67
  * @param attribute Attribute to check
73
68
  * @returns `true` if attribute is empty and not a boolean attribute
74
69
  */
75
- export function isEmptyNonBooleanAttribute(
76
- attribute: Attr | Attribute,
77
- ): boolean;
70
+ export function isEmptyNonBooleanAttribute(attribute: Attr | Attribute): boolean;
78
71
 
79
72
  /**
80
73
  * Is the attribute empty and not a boolean attribute?
@@ -82,10 +75,7 @@ export function isEmptyNonBooleanAttribute(
82
75
  * @param value Attribute value
83
76
  * @returns `true` if attribute is empty and not a boolean attribute
84
77
  */
85
- export function isEmptyNonBooleanAttribute(
86
- name: string,
87
- value: string,
88
- ): boolean;
78
+ export function isEmptyNonBooleanAttribute(name: string, value: string): boolean;
89
79
 
90
80
  export function isEmptyNonBooleanAttribute(
91
81
  first: string | Attr | Attribute,
@@ -144,9 +134,7 @@ export function isInvalidBooleanAttribute(
144
134
  }
145
135
 
146
136
  export function isProperty(value: unknown): value is Property {
147
- return (
148
- isPlainObject(value) && typeof (value as PlainObject).name === 'string'
149
- );
137
+ return isPlainObject(value) && typeof (value as PlainObject).name === 'string';
150
138
  }
151
139
 
152
140
  function isValidAttribute(
@@ -165,11 +153,7 @@ function isValidAttribute(
165
153
  return callback(attribute);
166
154
  }
167
155
 
168
- function updateAttribute(
169
- element: HTMLOrSVGElement,
170
- name: string,
171
- value: unknown,
172
- ): void {
156
+ function updateAttribute(element: HTMLOrSVGElement, name: string, value: unknown): void {
173
157
  const isBoolean = booleanAttributes.includes(name.toLowerCase());
174
158
 
175
159
  if (isBoolean) {
@@ -183,34 +167,20 @@ function updateAttribute(
183
167
  }
184
168
  }
185
169
 
186
- function updateProperty(
187
- element: HTMLOrSVGElement,
188
- name: string,
189
- value: unknown,
190
- ): void {
170
+ function updateProperty(element: HTMLOrSVGElement, name: string, value: unknown): void {
191
171
  const actual = name.toLowerCase();
192
172
 
193
173
  (element as unknown as PlainObject)[actual] =
194
- value === '' ||
195
- (typeof value === 'string' && value.toLowerCase() === actual) ||
196
- value === true;
174
+ value === '' || (typeof value === 'string' && value.toLowerCase() === actual) || value === true;
197
175
  }
198
176
 
199
- export function updateValue(
200
- element: HTMLOrSVGElement,
201
- first: unknown,
202
- second: unknown,
203
- ): void {
177
+ export function updateValue(element: HTMLOrSVGElement, first: unknown, second: unknown): void {
204
178
  if (!isHTMLOrSVGElement(element)) {
205
179
  return;
206
180
  }
207
181
 
208
182
  if (isProperty(first)) {
209
- updateAttribute(
210
- element,
211
- (first as Attribute).name,
212
- (first as Attribute).value,
213
- );
183
+ updateAttribute(element, (first as Attribute).name, (first as Attribute).value);
214
184
  } else if (typeof first === 'string') {
215
185
  updateAttribute(element, first as string, second);
216
186
  }
@@ -232,11 +202,7 @@ export function updateValues(
232
202
  const entry = entries[index];
233
203
 
234
204
  if (isArray) {
235
- updateAttribute(
236
- element,
237
- (entry[1] as Attribute).name,
238
- (entry[1] as Attribute).value,
239
- );
205
+ updateAttribute(element, (entry[1] as Attribute).name, (entry[1] as Attribute).value);
240
206
  } else {
241
207
  updateAttribute(element, entry[0], entry[1]);
242
208
  }
@@ -27,7 +27,6 @@ export function setElementValues(
27
27
  }
28
28
  }
29
29
 
30
- // biome-ignore lint/nursery/useMaxParams: Internal function, so it's fine
31
30
  export function updateElementValue(
32
31
  element: HTMLOrSVGElement,
33
32
  key: string,
@@ -14,9 +14,7 @@ export function getAttributeValue(
14
14
  const attribute = element.attributes[normalized as keyof NamedNodeMap];
15
15
  const value = attribute instanceof Attr ? attribute.value : undefined;
16
16
 
17
- return EXPRESSION_DATA_PREFIX.test(normalized) &&
18
- typeof value === 'string' &&
19
- parseValue
17
+ return EXPRESSION_DATA_PREFIX.test(normalized) && typeof value === 'string' && parseValue
20
18
  ? (parse(value) ?? value)
21
19
  : value;
22
20
  }
@@ -28,9 +26,7 @@ export function getStyleValue(
28
26
  ): string | undefined {
29
27
  const name = camelCase(property);
30
28
 
31
- return computed
32
- ? getComputedStyle(element)[name as never]
33
- : element.style[name as never];
29
+ return computed ? getComputedStyle(element)[name as never] : element.style[name as never];
34
30
  }
35
31
 
36
32
  export const EXPRESSION_DATA_PREFIX = /^data-/i;
@@ -1,41 +1,6 @@
1
- import {isPlainObject} from '@oscarpalmer/atoms/is';
2
- import {
3
- isBadAttribute,
4
- isEmptyNonBooleanAttribute,
5
- isInvalidBooleanAttribute,
6
- } from './attribute';
1
+ import {isBadAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute} from './attribute';
7
2
 
8
- //
9
-
10
- type Options = Required<SanitizeOptions>;
11
-
12
- export type SanitizeOptions = {
13
- /**
14
- * Sanitize boolean attributes? _(Defaults to `true`)_
15
- *
16
- * E.g. `checked="abc"` => `checked=""`
17
- */
18
- sanitizeBooleanAttributes?: boolean;
19
- };
20
-
21
- //
22
-
23
- export function getSanitizeOptions(input?: SanitizeOptions): Options {
24
- const options = isPlainObject(input) ? input : {};
25
-
26
- options.sanitizeBooleanAttributes =
27
- typeof options.sanitizeBooleanAttributes === 'boolean'
28
- ? options.sanitizeBooleanAttributes
29
- : true;
30
-
31
- return options as Options;
32
- }
33
-
34
- export function sanitizeAttributes(
35
- element: Element,
36
- attributes: Attr[],
37
- options: Options,
38
- ): void {
3
+ export function sanitizeAttributes(element: Element, attributes: Attr[]): void {
39
4
  const {length} = attributes;
40
5
 
41
6
  for (let index = 0; index < length; index += 1) {
@@ -43,16 +8,13 @@ export function sanitizeAttributes(
43
8
 
44
9
  if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) {
45
10
  element.removeAttribute(attribute.name);
46
- } else if (
47
- options.sanitizeBooleanAttributes &&
48
- isInvalidBooleanAttribute(attribute)
49
- ) {
11
+ } else if (isInvalidBooleanAttribute(attribute)) {
50
12
  element.setAttribute(attribute.name, '');
51
13
  }
52
14
  }
53
15
  }
54
16
 
55
- export function sanitizeNodes(nodes: Node[], options: Options): Node[] {
17
+ export function sanitizeNodes(nodes: Node[]): Node[] {
56
18
  const actual = nodes.filter(node => node instanceof Node);
57
19
  const {length} = nodes;
58
20
 
@@ -66,11 +28,11 @@ export function sanitizeNodes(nodes: Node[], options: Options): Node[] {
66
28
  script.remove();
67
29
  }
68
30
 
69
- sanitizeAttributes(node, [...node.attributes], options);
31
+ sanitizeAttributes(node, [...node.attributes]);
70
32
  }
71
33
 
72
34
  if (node.hasChildNodes()) {
73
- sanitizeNodes([...node.childNodes], options);
35
+ sanitizeNodes([...node.childNodes]);
74
36
  }
75
37
  }
76
38
 
package/src/is.ts CHANGED
@@ -48,4 +48,4 @@ const CHILD_NODE_TYPES: Set<number> = new Set([
48
48
 
49
49
  //
50
50
 
51
- export {isEventTarget, isHTMLOrSVGElement} from './internal/is'
51
+ export {isEventTarget, isHTMLOrSVGElement} from './internal/is';
package/src/style.ts CHANGED
@@ -57,11 +57,7 @@ export function getStyles<Property extends keyof CSSStyleDeclaration>(
57
57
  const property = properties[index];
58
58
 
59
59
  if (typeof property === 'string') {
60
- styles[property] = getStyleValue(
61
- element,
62
- property,
63
- computed === true,
64
- ) as never;
60
+ styles[property] = getStyleValue(element, property, computed === true) as never;
65
61
  }
66
62
  }
67
63
 
@@ -74,10 +70,7 @@ export function getStyles<Property extends keyof CSSStyleDeclaration>(
74
70
  * @param computed Get the computed text direction? _(defaults to `false`)_
75
71
  * @returns Text direction
76
72
  */
77
- export function getTextDirection(
78
- element: HTMLOrSVGElement,
79
- computed?: boolean,
80
- ): TextDirection {
73
+ export function getTextDirection(element: HTMLOrSVGElement, computed?: boolean): TextDirection {
81
74
  if (!(element instanceof Element)) {
82
75
  return undefined as never;
83
76
  }
@@ -112,10 +105,7 @@ export function setStyle(
112
105
  * @param element Element to set the styles on
113
106
  * @param styles Styles to set
114
107
  */
115
- export function setStyles(
116
- element: HTMLOrSVGElement,
117
- styles: Partial<CSSStyleDeclaration>,
118
- ): void {
108
+ export function setStyles(element: HTMLOrSVGElement, styles: Partial<CSSStyleDeclaration>): void {
119
109
  setElementValues(element, styles as never, null, updateStyleProperty);
120
110
  }
121
111
 
@@ -170,11 +160,7 @@ export function toggleStyles(
170
160
  };
171
161
  }
172
162
 
173
- function updateStyleProperty(
174
- element: HTMLOrSVGElement,
175
- key: string,
176
- value: unknown,
177
- ): void {
163
+ function updateStyleProperty(element: HTMLOrSVGElement, key: string, value: unknown): void {
178
164
  updateElementValue(
179
165
  element,
180
166
  key,
package/src/touch.ts CHANGED
@@ -36,16 +36,12 @@ function getSupport(): boolean {
36
36
  return true;
37
37
  }
38
38
 
39
- if (
40
- typeof navigator.maxTouchPoints === 'number' &&
41
- navigator.maxTouchPoints > 0
42
- ) {
39
+ if (typeof navigator.maxTouchPoints === 'number' && navigator.maxTouchPoints > 0) {
43
40
  return true;
44
41
  }
45
42
 
46
43
  if (
47
- typeof (navigator as NavigatorWithMsMaxTouchPoints).msMaxTouchPoints ===
48
- 'number' &&
44
+ typeof (navigator as NavigatorWithMsMaxTouchPoints).msMaxTouchPoints === 'number' &&
49
45
  (navigator as NavigatorWithMsMaxTouchPoints).msMaxTouchPoints > 0
50
46
  ) {
51
47
  return true;
package/types/html.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { type SanitizeOptions } from './internal/sanitize';
2
1
  type Html = {
3
2
  /**
4
3
  * Create nodes from an HTML string or a template element
@@ -29,7 +28,7 @@ type HtmlOptions = {
29
28
  * Ignore caching the template element for the HTML string? _(defaults to `false`)_
30
29
  */
31
30
  ignoreCache?: boolean;
32
- } & SanitizeOptions;
31
+ };
33
32
  declare const html: Html;
34
33
  /**
35
34
  * Sanitize one or more nodes, recursively
@@ -37,5 +36,5 @@ declare const html: Html;
37
36
  * @param options Sanitization options
38
37
  * @returns Sanitized nodes
39
38
  */
40
- export declare function sanitize(value: Node | Node[], options?: SanitizeOptions): Node[];
39
+ export declare function sanitize(value: Node | Node[]): Node[];
41
40
  export { html };
@@ -1,13 +1,2 @@
1
- type Options = Required<SanitizeOptions>;
2
- export type SanitizeOptions = {
3
- /**
4
- * Sanitize boolean attributes? _(Defaults to `true`)_
5
- *
6
- * E.g. `checked="abc"` => `checked=""`
7
- */
8
- sanitizeBooleanAttributes?: boolean;
9
- };
10
- export declare function getSanitizeOptions(input?: SanitizeOptions): Options;
11
- export declare function sanitizeAttributes(element: Element, attributes: Attr[], options: Options): void;
12
- export declare function sanitizeNodes(nodes: Node[], options: Options): Node[];
13
- export {};
1
+ export declare function sanitizeAttributes(element: Element, attributes: Attr[]): void;
2
+ export declare function sanitizeNodes(nodes: Node[]): Node[];