@oscarpalmer/toretto 0.28.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/dist/html.js CHANGED
@@ -1,37 +1,42 @@
1
- import { getSanitizeOptions, sanitizeNodes } from "./internal/sanitize.js";
1
+ import { sanitizeNodes } from "./internal/sanitize.js";
2
2
  import { isPlainObject } from "@oscarpalmer/atoms/is";
3
- function createTemplate(html$1, ignore) {
4
- const template = document.createElement("template");
5
- template.innerHTML = html$1;
6
- if (!ignore) templates[html$1] = template;
3
+ function createHtml(value) {
4
+ const html$1 = getParser().parseFromString(typeof value === "string" ? value : value.innerHTML, HTML_PARSE_TYPE);
5
+ html$1.body.normalize();
6
+ sanitizeNodes([html$1.body]);
7
+ return html$1.body.innerHTML;
8
+ }
9
+ function createTemplate(value, options) {
10
+ const template = document.createElement(TEMPLATE_TAG);
11
+ template.innerHTML = createHtml(value);
12
+ if (typeof value === "string" && !options.ignoreCache) templates[value] = template;
7
13
  return template;
8
14
  }
9
- function getHtml(value, options) {
15
+ function getNodes(value, options) {
10
16
  if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
11
- const template = value instanceof HTMLTemplateElement ? value : getTemplate(value, options.ignoreCache);
12
- if (template == null) return [];
13
- const cloned = template.content.cloneNode(true);
14
- const scripts = cloned.querySelectorAll("script");
15
- for (const script of scripts) script.remove();
16
- cloned.normalize();
17
- return sanitizeNodes([...cloned.childNodes], options);
17
+ const template = getTemplate(value, options);
18
+ return template == null ? [] : [...template.content.cloneNode(true).childNodes];
18
19
  }
19
20
  function getOptions(input) {
20
21
  const options = isPlainObject(input) ? input : {};
21
22
  options.ignoreCache = typeof options.ignoreCache === "boolean" ? options.ignoreCache : false;
22
- options.sanitizeBooleanAttributes = typeof options.sanitizeBooleanAttributes === "boolean" ? options.sanitizeBooleanAttributes : true;
23
23
  return options;
24
24
  }
25
- function getTemplate(value, ignore) {
25
+ function getParser() {
26
+ parser ??= new DOMParser();
27
+ return parser;
28
+ }
29
+ function getTemplate(value, options) {
30
+ if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
26
31
  if (typeof value !== "string" || value.trim().length === 0) return;
27
32
  let template = templates[value];
28
33
  if (template != null) return template;
29
34
  const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
30
- template = element instanceof HTMLTemplateElement ? element : createTemplate(value, ignore);
35
+ template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
31
36
  return template;
32
37
  }
33
38
  var html = ((value, options) => {
34
- return getHtml(value, getOptions(options));
39
+ return getNodes(value, getOptions(options));
35
40
  });
36
41
  html.clear = () => {
37
42
  templates = {};
@@ -47,9 +52,12 @@ html.remove = (template) => {
47
52
  }
48
53
  templates = updated;
49
54
  };
50
- function sanitize(value, options) {
51
- return sanitizeNodes(Array.isArray(value) ? value : [value], getSanitizeOptions(options));
55
+ function sanitize(value) {
56
+ return sanitizeNodes(Array.isArray(value) ? value : [value]);
52
57
  }
53
58
  var EXPRESSION_ID = /^[a-z][\w-]*$/i;
59
+ var HTML_PARSE_TYPE = "text/html";
60
+ var TEMPLATE_TAG = "template";
61
+ var parser;
54
62
  var templates = {};
55
63
  export { html, sanitize };
@@ -1,19 +1,13 @@
1
1
  import { isBadAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "./attribute.js";
2
- import { isPlainObject } from "@oscarpalmer/atoms/is";
3
- function getSanitizeOptions(input) {
4
- const options = isPlainObject(input) ? input : {};
5
- options.sanitizeBooleanAttributes = typeof options.sanitizeBooleanAttributes === "boolean" ? options.sanitizeBooleanAttributes : true;
6
- return options;
7
- }
8
- function sanitizeAttributes(element, attributes, options) {
2
+ function sanitizeAttributes(element, attributes) {
9
3
  const { length } = attributes;
10
4
  for (let index = 0; index < length; index += 1) {
11
5
  const attribute = attributes[index];
12
6
  if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) element.removeAttribute(attribute.name);
13
- else if (options.sanitizeBooleanAttributes && isInvalidBooleanAttribute(attribute)) element.setAttribute(attribute.name, "");
7
+ else if (isInvalidBooleanAttribute(attribute)) element.setAttribute(attribute.name, "");
14
8
  }
15
9
  }
16
- function sanitizeNodes(nodes, options) {
10
+ function sanitizeNodes(nodes) {
17
11
  const actual = nodes.filter((node) => node instanceof Node);
18
12
  const { length } = nodes;
19
13
  for (let index = 0; index < length; index += 1) {
@@ -21,10 +15,10 @@ function sanitizeNodes(nodes, options) {
21
15
  if (node instanceof Element) {
22
16
  const scripts = node.querySelectorAll("script");
23
17
  for (const script of scripts) script.remove();
24
- sanitizeAttributes(node, [...node.attributes], options);
18
+ sanitizeAttributes(node, [...node.attributes]);
25
19
  }
26
- if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], options);
20
+ if (node.hasChildNodes()) sanitizeNodes([...node.childNodes]);
27
21
  }
28
22
  return nodes;
29
23
  }
30
- export { getSanitizeOptions, sanitizeAttributes, sanitizeNodes };
24
+ export { sanitizeAttributes, sanitizeNodes };
@@ -1075,28 +1075,19 @@ const TABINDEX_BASE = 0;
1075
1075
  const TABINDEX_DEFAULT = -1;
1076
1076
  const TYPE_RADIO = 'radio';
1077
1077
 
1078
- //
1079
- function getSanitizeOptions(input) {
1080
- const options = isPlainObject(input) ? input : {};
1081
- options.sanitizeBooleanAttributes =
1082
- typeof options.sanitizeBooleanAttributes === 'boolean'
1083
- ? options.sanitizeBooleanAttributes
1084
- : true;
1085
- return options;
1086
- }
1087
- function sanitizeAttributes(element, attributes, options) {
1078
+ function sanitizeAttributes(element, attributes) {
1088
1079
  const { length } = attributes;
1089
1080
  for (let index = 0; index < length; index += 1) {
1090
1081
  const attribute = attributes[index];
1091
1082
  if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) {
1092
1083
  element.removeAttribute(attribute.name);
1093
1084
  }
1094
- else if (options.sanitizeBooleanAttributes && isInvalidBooleanAttribute(attribute)) {
1085
+ else if (isInvalidBooleanAttribute(attribute)) {
1095
1086
  element.setAttribute(attribute.name, '');
1096
1087
  }
1097
1088
  }
1098
1089
  }
1099
- function sanitizeNodes(nodes, options) {
1090
+ function sanitizeNodes(nodes) {
1100
1091
  const actual = nodes.filter(node => node instanceof Node);
1101
1092
  const { length } = nodes;
1102
1093
  for (let index = 0; index < length; index += 1) {
@@ -1106,50 +1097,50 @@ function sanitizeNodes(nodes, options) {
1106
1097
  for (const script of scripts) {
1107
1098
  script.remove();
1108
1099
  }
1109
- sanitizeAttributes(node, [...node.attributes], options);
1100
+ sanitizeAttributes(node, [...node.attributes]);
1110
1101
  }
1111
1102
  if (node.hasChildNodes()) {
1112
- sanitizeNodes([...node.childNodes], options);
1103
+ sanitizeNodes([...node.childNodes]);
1113
1104
  }
1114
1105
  }
1115
1106
  return nodes;
1116
1107
  }
1117
1108
 
1118
1109
  //
1119
- function createTemplate(html, ignore) {
1120
- const template = document.createElement('template');
1121
- template.innerHTML = html;
1122
- if (!ignore) {
1123
- templates[html] = template;
1110
+ function createHtml(value) {
1111
+ const html = getParser().parseFromString(typeof value === 'string' ? value : value.innerHTML, HTML_PARSE_TYPE);
1112
+ html.body.normalize();
1113
+ sanitizeNodes([html.body]);
1114
+ return html.body.innerHTML;
1115
+ }
1116
+ function createTemplate(value, options) {
1117
+ const template = document.createElement(TEMPLATE_TAG);
1118
+ template.innerHTML = createHtml(value);
1119
+ if (typeof value === 'string' && !options.ignoreCache) {
1120
+ templates[value] = template;
1124
1121
  }
1125
1122
  return template;
1126
1123
  }
1127
- function getHtml(value, options) {
1124
+ function getNodes(value, options) {
1128
1125
  if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
1129
1126
  return [];
1130
1127
  }
1131
- const template = value instanceof HTMLTemplateElement ? value : getTemplate(value, options.ignoreCache);
1132
- if (template == null) {
1133
- return [];
1134
- }
1135
- const cloned = template.content.cloneNode(true);
1136
- const scripts = cloned.querySelectorAll('script');
1137
- for (const script of scripts) {
1138
- script.remove();
1139
- }
1140
- cloned.normalize();
1141
- return sanitizeNodes([...cloned.childNodes], options);
1128
+ const template = getTemplate(value, options);
1129
+ return template == null ? [] : [...template.content.cloneNode(true).childNodes];
1142
1130
  }
1143
1131
  function getOptions(input) {
1144
1132
  const options = isPlainObject(input) ? input : {};
1145
1133
  options.ignoreCache = typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
1146
- options.sanitizeBooleanAttributes =
1147
- typeof options.sanitizeBooleanAttributes === 'boolean'
1148
- ? options.sanitizeBooleanAttributes
1149
- : true;
1150
1134
  return options;
1151
1135
  }
1152
- function getTemplate(value, ignore) {
1136
+ function getParser() {
1137
+ parser ??= new DOMParser();
1138
+ return parser;
1139
+ }
1140
+ function getTemplate(value, options) {
1141
+ if (value instanceof HTMLTemplateElement) {
1142
+ return createTemplate(value, options);
1143
+ }
1153
1144
  if (typeof value !== 'string' || value.trim().length === 0) {
1154
1145
  return;
1155
1146
  }
@@ -1158,11 +1149,11 @@ function getTemplate(value, ignore) {
1158
1149
  return template;
1159
1150
  }
1160
1151
  const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
1161
- template = element instanceof HTMLTemplateElement ? element : createTemplate(value, ignore);
1152
+ template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
1162
1153
  return template;
1163
1154
  }
1164
1155
  const html = ((value, options) => {
1165
- return getHtml(value, getOptions(options));
1156
+ return getNodes(value, getOptions(options));
1166
1157
  });
1167
1158
  html.clear = () => {
1168
1159
  templates = {};
@@ -1188,11 +1179,14 @@ html.remove = (template) => {
1188
1179
  * @param options Sanitization options
1189
1180
  * @returns Sanitized nodes
1190
1181
  */
1191
- function sanitize(value, options) {
1192
- return sanitizeNodes(Array.isArray(value) ? value : [value], getSanitizeOptions(options));
1182
+ function sanitize(value) {
1183
+ return sanitizeNodes(Array.isArray(value) ? value : [value]);
1193
1184
  }
1194
1185
  //
1195
1186
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
1187
+ const HTML_PARSE_TYPE = 'text/html';
1188
+ const TEMPLATE_TAG = 'template';
1189
+ let parser;
1196
1190
  let templates = {};
1197
1191
 
1198
1192
  /**
package/package.json CHANGED
@@ -93,5 +93,5 @@
93
93
  },
94
94
  "type": "module",
95
95
  "types": "types/index.d.ts",
96
- "version": "0.28.0"
96
+ "version": "0.29.0"
97
97
  }
package/src/html.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
- import {getSanitizeOptions, type SanitizeOptions, sanitizeNodes} from './internal/sanitize';
2
+ import {sanitizeNodes} from './internal/sanitize';
3
3
 
4
4
  //
5
5
 
@@ -37,47 +37,48 @@ type HtmlOptions = {
37
37
  * Ignore caching the template element for the HTML string? _(defaults to `false`)_
38
38
  */
39
39
  ignoreCache?: boolean;
40
- } & SanitizeOptions;
40
+ };
41
41
 
42
42
  type Options = Required<HtmlOptions>;
43
43
 
44
44
  //
45
45
 
46
- function createTemplate(html: string, ignore: boolean): HTMLTemplateElement {
47
- 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
+ );
48
51
 
49
- template.innerHTML = html;
52
+ html.body.normalize();
50
53
 
51
- if (!ignore) {
52
- templates[html] = template;
53
- }
54
+ sanitizeNodes([html.body]);
54
55
 
55
- return template;
56
+ return html.body.innerHTML;
56
57
  }
57
58
 
58
- function getHtml(value: string | HTMLTemplateElement, options: Options): Node[] {
59
- if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
60
- return [];
61
- }
59
+ function createTemplate(
60
+ value: string | HTMLTemplateElement,
61
+ options: Options,
62
+ ): HTMLTemplateElement {
63
+ const template = document.createElement(TEMPLATE_TAG);
62
64
 
63
- const template =
64
- value instanceof HTMLTemplateElement ? value : getTemplate(value, options.ignoreCache);
65
+ template.innerHTML = createHtml(value);
65
66
 
66
- if (template == null) {
67
- return [];
67
+ if (typeof value === 'string' && !options.ignoreCache) {
68
+ templates[value] = template;
68
69
  }
69
70
 
70
- const cloned = template.content.cloneNode(true) as DocumentFragment;
71
-
72
- const scripts = cloned.querySelectorAll('script');
71
+ return template;
72
+ }
73
73
 
74
- for (const script of scripts) {
75
- script.remove();
74
+ function getNodes(value: string | HTMLTemplateElement, options: Options): Node[] {
75
+ if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
76
+ return [];
76
77
  }
77
78
 
78
- cloned.normalize();
79
+ const template = getTemplate(value, options);
79
80
 
80
- return sanitizeNodes([...cloned.childNodes], options);
81
+ return template == null ? [] : [...template.content.cloneNode(true).childNodes];
81
82
  }
82
83
 
83
84
  function getOptions(input?: HtmlOptions): Options {
@@ -85,15 +86,23 @@ function getOptions(input?: HtmlOptions): Options {
85
86
 
86
87
  options.ignoreCache = typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
87
88
 
88
- options.sanitizeBooleanAttributes =
89
- typeof options.sanitizeBooleanAttributes === 'boolean'
90
- ? options.sanitizeBooleanAttributes
91
- : true;
92
-
93
89
  return options as Options;
94
90
  }
95
91
 
96
- function getTemplate(value: string, ignore: boolean): HTMLTemplateElement | undefined {
92
+ function getParser(): DOMParser {
93
+ parser ??= new DOMParser();
94
+
95
+ return parser;
96
+ }
97
+
98
+ function getTemplate(
99
+ value: string | HTMLTemplateElement,
100
+ options: Options,
101
+ ): HTMLTemplateElement | undefined {
102
+ if (value instanceof HTMLTemplateElement) {
103
+ return createTemplate(value, options);
104
+ }
105
+
97
106
  if (typeof value !== 'string' || value.trim().length === 0) {
98
107
  return;
99
108
  }
@@ -106,13 +115,13 @@ function getTemplate(value: string, ignore: boolean): HTMLTemplateElement | unde
106
115
 
107
116
  const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
108
117
 
109
- template = element instanceof HTMLTemplateElement ? element : createTemplate(value, ignore);
118
+ template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
110
119
 
111
120
  return template;
112
121
  }
113
122
 
114
123
  const html = ((value: string | HTMLTemplateElement, options?: Options): Node[] => {
115
- return getHtml(value, getOptions(options));
124
+ return getNodes(value, getOptions(options));
116
125
  }) as Html;
117
126
 
118
127
  html.clear = (): void => {
@@ -146,14 +155,20 @@ html.remove = (template: string): void => {
146
155
  * @param options Sanitization options
147
156
  * @returns Sanitized nodes
148
157
  */
149
- export function sanitize(value: Node | Node[], options?: SanitizeOptions): Node[] {
150
- return sanitizeNodes(Array.isArray(value) ? value : [value], getSanitizeOptions(options));
158
+ export function sanitize(value: Node | Node[]): Node[] {
159
+ return sanitizeNodes(Array.isArray(value) ? value : [value]);
151
160
  }
152
161
 
153
162
  //
154
163
 
155
164
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
156
165
 
166
+ const HTML_PARSE_TYPE = 'text/html';
167
+
168
+ const TEMPLATE_TAG = 'template';
169
+
170
+ let parser: DOMParser;
171
+
157
172
  let templates: Record<string, HTMLTemplateElement> = {};
158
173
 
159
174
  //
@@ -1,33 +1,6 @@
1
- import {isPlainObject} from '@oscarpalmer/atoms/is';
2
1
  import {isBadAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute} from './attribute';
3
2
 
4
- //
5
-
6
- type Options = Required<SanitizeOptions>;
7
-
8
- export type SanitizeOptions = {
9
- /**
10
- * Sanitize boolean attributes? _(Defaults to `true`)_
11
- *
12
- * E.g. `checked="abc"` => `checked=""`
13
- */
14
- sanitizeBooleanAttributes?: boolean;
15
- };
16
-
17
- //
18
-
19
- export function getSanitizeOptions(input?: SanitizeOptions): Options {
20
- const options = isPlainObject(input) ? input : {};
21
-
22
- options.sanitizeBooleanAttributes =
23
- typeof options.sanitizeBooleanAttributes === 'boolean'
24
- ? options.sanitizeBooleanAttributes
25
- : true;
26
-
27
- return options as Options;
28
- }
29
-
30
- export function sanitizeAttributes(element: Element, attributes: Attr[], options: Options): void {
3
+ export function sanitizeAttributes(element: Element, attributes: Attr[]): void {
31
4
  const {length} = attributes;
32
5
 
33
6
  for (let index = 0; index < length; index += 1) {
@@ -35,13 +8,13 @@ export function sanitizeAttributes(element: Element, attributes: Attr[], options
35
8
 
36
9
  if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) {
37
10
  element.removeAttribute(attribute.name);
38
- } else if (options.sanitizeBooleanAttributes && isInvalidBooleanAttribute(attribute)) {
11
+ } else if (isInvalidBooleanAttribute(attribute)) {
39
12
  element.setAttribute(attribute.name, '');
40
13
  }
41
14
  }
42
15
  }
43
16
 
44
- export function sanitizeNodes(nodes: Node[], options: Options): Node[] {
17
+ export function sanitizeNodes(nodes: Node[]): Node[] {
45
18
  const actual = nodes.filter(node => node instanceof Node);
46
19
  const {length} = nodes;
47
20
 
@@ -55,11 +28,11 @@ export function sanitizeNodes(nodes: Node[], options: Options): Node[] {
55
28
  script.remove();
56
29
  }
57
30
 
58
- sanitizeAttributes(node, [...node.attributes], options);
31
+ sanitizeAttributes(node, [...node.attributes]);
59
32
  }
60
33
 
61
34
  if (node.hasChildNodes()) {
62
- sanitizeNodes([...node.childNodes], options);
35
+ sanitizeNodes([...node.childNodes]);
63
36
  }
64
37
  }
65
38
 
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[];