@oscarpalmer/toretto 0.24.0 → 0.25.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,38 +1,51 @@
1
- import { sanitize } from "./sanitize.js";
2
- function createTemplate(html$1) {
1
+ import { sanitizeNodes } from "./internal/sanitize.js";
2
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
3
+ function createTemplate(html$1, ignore) {
3
4
  const template = document.createElement("template");
4
5
  template.innerHTML = html$1;
5
- templates[html$1] = template;
6
+ if (!ignore) templates[html$1] = template;
6
7
  return template;
7
8
  }
8
- function getTemplate(value) {
9
+ function getHtml(value, options) {
10
+ 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);
18
+ }
19
+ function getOptions(input) {
20
+ const options = isPlainObject(input) ? input : {};
21
+ options.ignoreCache = typeof options.ignoreCache === "boolean" ? options.ignoreCache : false;
22
+ options.sanitizeBooleanAttributes = typeof options.sanitizeBooleanAttributes === "boolean" ? options.sanitizeBooleanAttributes : true;
23
+ return options;
24
+ }
25
+ function getTemplate(value, ignore) {
9
26
  if (typeof value !== "string" || value.trim().length === 0) return;
10
27
  let template = templates[value];
11
28
  if (template != null) return template;
12
29
  const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
13
- template = element instanceof HTMLTemplateElement ? element : createTemplate(value);
14
- templates[value] = template;
30
+ template = element instanceof HTMLTemplateElement ? element : createTemplate(value, ignore);
15
31
  return template;
16
32
  }
17
- var html = ((value, sanitization) => {
18
- if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
19
- let options;
20
- if (sanitization == null || sanitization === true) options = {};
21
- else options = sanitization === false ? void 0 : sanitization;
22
- const template = value instanceof HTMLTemplateElement ? value : getTemplate(value);
23
- if (template == null) return [];
24
- const cloned = template.content.cloneNode(true);
25
- const scripts = cloned.querySelectorAll("script");
26
- const { length } = scripts;
27
- for (let index = 0; index < length; index += 1) scripts[index].remove();
28
- cloned.normalize();
29
- return options != null ? sanitize([...cloned.childNodes], options) : [...cloned.childNodes];
33
+ var html = ((value, options) => {
34
+ return getHtml(value, getOptions(options));
30
35
  });
31
36
  html.clear = () => {
32
37
  templates = {};
33
38
  };
34
39
  html.remove = (template) => {
35
- if (typeof template === "string") templates[template] = void 0;
40
+ if (typeof template !== "string" || templates[template] == null) return;
41
+ const keys = Object.keys(templates);
42
+ const { length } = keys;
43
+ const updated = {};
44
+ for (let index = 0; index < length; index += 1) {
45
+ const key = keys[index];
46
+ if (key !== template) updated[key] = templates[key];
47
+ }
48
+ templates = updated;
36
49
  };
37
50
  var EXPRESSION_ID = /^[a-z][\w-]*$/i;
38
51
  var templates = {};
@@ -0,0 +1,30 @@
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) {
9
+ const { length } = attributes;
10
+ for (let index = 0; index < length; index += 1) {
11
+ const attribute = attributes[index];
12
+ if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) element.removeAttribute(attribute.name);
13
+ else if (options.sanitizeBooleanAttributes && isInvalidBooleanAttribute(attribute)) element.setAttribute(attribute.name, "");
14
+ }
15
+ }
16
+ function sanitizeNodes(nodes, options) {
17
+ const actual = nodes.filter((node) => node instanceof Node);
18
+ const { length } = nodes;
19
+ for (let index = 0; index < length; index += 1) {
20
+ const node = actual[index];
21
+ if (node instanceof Element) {
22
+ const scripts = node.querySelectorAll("script");
23
+ for (const script of scripts) script.remove();
24
+ sanitizeAttributes(node, [...node.attributes], options);
25
+ }
26
+ if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], options);
27
+ }
28
+ return nodes;
29
+ }
30
+ export { getSanitizeOptions, sanitizeAttributes, sanitizeNodes };
package/dist/sanitize.js CHANGED
@@ -1,29 +1,5 @@
1
- import { isBadAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "./attribute.js";
2
- import { isPlainObject } from "@oscarpalmer/atoms/is";
3
- function getOptions(input) {
4
- const options = isPlainObject(input) ? input : {};
5
- options.sanitizeBooleanAttributes = typeof options.sanitizeBooleanAttributes === "boolean" ? options.sanitizeBooleanAttributes : true;
6
- return options;
7
- }
1
+ import { getSanitizeOptions, sanitizeNodes } from "./internal/sanitize.js";
8
2
  function sanitize(value, options) {
9
- return sanitizeNodes(Array.isArray(value) ? value : [value], getOptions(options));
10
- }
11
- function sanitizeAttributes(element, attributes, options) {
12
- const { length } = attributes;
13
- for (let index = 0; index < length; index += 1) {
14
- const attribute = attributes[index];
15
- if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) element.removeAttribute(attribute.name);
16
- else if (options.sanitizeBooleanAttributes && isInvalidBooleanAttribute(attribute)) element.setAttribute(attribute.name, "");
17
- }
18
- }
19
- function sanitizeNodes(nodes, options) {
20
- const actual = nodes.filter((node) => node instanceof Node);
21
- const { length } = nodes;
22
- for (let index = 0; index < length; index += 1) {
23
- const node = actual[index];
24
- if (node instanceof Element) sanitizeAttributes(node, [...node.attributes], options);
25
- if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], options);
26
- }
27
- return nodes;
3
+ return sanitizeNodes(Array.isArray(value) ? value : [value], getSanitizeOptions(options));
28
4
  }
29
5
  export { sanitize };
@@ -437,10 +437,8 @@ function calculate() {
437
437
  function step(now) {
438
438
  if (last != null) values.push(now - last);
439
439
  last = now;
440
- if (values.length >= CALCULATION_TOTAL) {
441
- const median = values.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - CALCULATION_TRIM);
442
- resolve(median);
443
- } else requestAnimationFrame(step);
440
+ if (values.length >= CALCULATION_TOTAL) resolve(values.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - CALCULATION_TRIM));
441
+ else requestAnimationFrame(step);
444
442
  }
445
443
  requestAnimationFrame(step);
446
444
  });
@@ -988,7 +986,8 @@ const TABINDEX_BASE = 0;
988
986
  const TABINDEX_DEFAULT = -1;
989
987
  const TYPE_RADIO = 'radio';
990
988
 
991
- function getOptions(input) {
989
+ //
990
+ function getSanitizeOptions(input) {
992
991
  const options = isPlainObject(input) ? input : {};
993
992
  options.sanitizeBooleanAttributes =
994
993
  typeof options.sanitizeBooleanAttributes === 'boolean'
@@ -996,15 +995,6 @@ function getOptions(input) {
996
995
  : true;
997
996
  return options;
998
997
  }
999
- /**
1000
- * Sanitize one or more nodes, recursively
1001
- * @param value Node or nodes to sanitize
1002
- * @param options Sanitization options
1003
- * @returns Sanitized nodes
1004
- */
1005
- function sanitize(value, options) {
1006
- return sanitizeNodes(Array.isArray(value) ? value : [value], getOptions(options));
1007
- }
1008
998
  function sanitizeAttributes(element, attributes, options) {
1009
999
  const { length } = attributes;
1010
1000
  for (let index = 0; index < length; index += 1) {
@@ -1024,6 +1014,10 @@ function sanitizeNodes(nodes, options) {
1024
1014
  for (let index = 0; index < length; index += 1) {
1025
1015
  const node = actual[index];
1026
1016
  if (node instanceof Element) {
1017
+ const scripts = node.querySelectorAll('script');
1018
+ for (const script of scripts) {
1019
+ script.remove();
1020
+ }
1027
1021
  sanitizeAttributes(node, [...node.attributes], options);
1028
1022
  }
1029
1023
  if (node.hasChildNodes()) {
@@ -1034,13 +1028,43 @@ function sanitizeNodes(nodes, options) {
1034
1028
  }
1035
1029
 
1036
1030
  //
1037
- function createTemplate(html) {
1031
+ function createTemplate(html, ignore) {
1038
1032
  const template = document.createElement('template');
1039
1033
  template.innerHTML = html;
1040
- templates[html] = template;
1034
+ if (!ignore) {
1035
+ templates[html] = template;
1036
+ }
1041
1037
  return template;
1042
1038
  }
1043
- function getTemplate(value) {
1039
+ function getHtml(value, options) {
1040
+ if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
1041
+ return [];
1042
+ }
1043
+ const template = value instanceof HTMLTemplateElement
1044
+ ? value
1045
+ : getTemplate(value, options.ignoreCache);
1046
+ if (template == null) {
1047
+ return [];
1048
+ }
1049
+ const cloned = template.content.cloneNode(true);
1050
+ const scripts = cloned.querySelectorAll('script');
1051
+ for (const script of scripts) {
1052
+ script.remove();
1053
+ }
1054
+ cloned.normalize();
1055
+ return sanitizeNodes([...cloned.childNodes], options);
1056
+ }
1057
+ function getOptions(input) {
1058
+ const options = isPlainObject(input) ? input : {};
1059
+ options.ignoreCache =
1060
+ typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
1061
+ options.sanitizeBooleanAttributes =
1062
+ typeof options.sanitizeBooleanAttributes === 'boolean'
1063
+ ? options.sanitizeBooleanAttributes
1064
+ : true;
1065
+ return options;
1066
+ }
1067
+ function getTemplate(value, ignore) {
1044
1068
  if (typeof value !== 'string' || value.trim().length === 0) {
1045
1069
  return;
1046
1070
  }
@@ -1052,48 +1076,46 @@ function getTemplate(value) {
1052
1076
  ? document.querySelector(`#${value}`)
1053
1077
  : null;
1054
1078
  template =
1055
- element instanceof HTMLTemplateElement ? element : createTemplate(value);
1056
- templates[value] = template;
1079
+ element instanceof HTMLTemplateElement
1080
+ ? element
1081
+ : createTemplate(value, ignore);
1057
1082
  return template;
1058
1083
  }
1059
- const html = ((value, sanitization) => {
1060
- if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
1061
- return [];
1062
- }
1063
- let options;
1064
- if (sanitization == null || sanitization === true) {
1065
- options = {};
1066
- }
1067
- else {
1068
- options = sanitization === false ? undefined : sanitization;
1069
- }
1070
- const template = value instanceof HTMLTemplateElement ? value : getTemplate(value);
1071
- if (template == null) {
1072
- return [];
1073
- }
1074
- const cloned = template.content.cloneNode(true);
1075
- const scripts = cloned.querySelectorAll('script');
1076
- const { length } = scripts;
1077
- for (let index = 0; index < length; index += 1) {
1078
- scripts[index].remove();
1079
- }
1080
- cloned.normalize();
1081
- return options != null
1082
- ? sanitize([...cloned.childNodes], options)
1083
- : [...cloned.childNodes];
1084
+ const html = ((value, options) => {
1085
+ return getHtml(value, getOptions(options));
1084
1086
  });
1085
1087
  html.clear = () => {
1086
1088
  templates = {};
1087
1089
  };
1088
1090
  html.remove = (template) => {
1089
- if (typeof template === 'string') {
1090
- templates[template] = undefined;
1091
+ if (typeof template !== 'string' || templates[template] == null) {
1092
+ return;
1093
+ }
1094
+ const keys = Object.keys(templates);
1095
+ const { length } = keys;
1096
+ const updated = {};
1097
+ for (let index = 0; index < length; index += 1) {
1098
+ const key = keys[index];
1099
+ if (key !== template) {
1100
+ updated[key] = templates[key];
1101
+ }
1091
1102
  }
1103
+ templates = updated;
1092
1104
  };
1093
1105
  //
1094
1106
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
1095
1107
  let templates = {};
1096
1108
 
1109
+ /**
1110
+ * Sanitize one or more nodes, recursively
1111
+ * @param value Node or nodes to sanitize
1112
+ * @param options Sanitization options
1113
+ * @returns Sanitized nodes
1114
+ */
1115
+ function sanitize(value, options) {
1116
+ return sanitizeNodes(Array.isArray(value) ? value : [value], getSanitizeOptions(options));
1117
+ }
1118
+
1097
1119
  /**
1098
1120
  * Get a style from an element
1099
1121
  * @param element Element to get the style from
package/package.json CHANGED
@@ -4,21 +4,22 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.112"
7
+ "@oscarpalmer/atoms": "^0.114"
8
8
  },
9
9
  "description": "A collection of badass DOM utilities.",
10
10
  "devDependencies": {
11
- "@biomejs/biome": "^2.2",
11
+ "@biomejs/biome": "^2.3",
12
12
  "@rollup/plugin-node-resolve": "^16",
13
- "@rollup/plugin-typescript": "^12.1",
14
- "@types/node": "^24.7",
15
- "@vitest/coverage-istanbul": "^3.2",
16
- "jsdom": "^27.0",
13
+ "@rollup/plugin-typescript": "^12.3",
14
+ "@types/node": "^24.10",
15
+ "@vitest/coverage-istanbul": "^4",
16
+ "glob": "^11",
17
+ "jsdom": "^27.1",
17
18
  "rollup": "^4.52",
18
19
  "tslib": "^2.8",
19
20
  "typescript": "^5.9",
20
21
  "vite": "npm:rolldown-vite@latest",
21
- "vitest": "^3.2"
22
+ "vitest": "^4"
22
23
  },
23
24
  "exports": {
24
25
  "./package.json": "./package.json",
@@ -96,5 +97,5 @@
96
97
  },
97
98
  "type": "module",
98
99
  "types": "types/index.d.ts",
99
- "version": "0.24.0"
100
+ "version": "0.25.0"
100
101
  }
package/src/html.ts CHANGED
@@ -1,4 +1,5 @@
1
- import {type SanitizeOptions, sanitize} from './sanitize';
1
+ import {isPlainObject} from '@oscarpalmer/atoms/is';
2
+ import {type SanitizeOptions, sanitizeNodes} from './internal/sanitize';
2
3
 
3
4
  //
4
5
 
@@ -6,21 +7,18 @@ type Html = {
6
7
  /**
7
8
  * Create nodes from an HTML string or a template element
8
9
  * @param value HTML string or id for a template element
9
- * @param sanitization Sanitization options
10
+ * @param options Options for creating nodes
10
11
  * @returns Created nodes
11
12
  */
12
- (value: string, sanitization?: boolean | SanitizeOptions): Node[];
13
+ (value: string, options?: HtmlOptions): Node[];
13
14
 
14
15
  /**
15
16
  * Create nodes from a template element
16
17
  * @param template Template element
17
- * @param sanitization Sanitization options
18
+ * @param options Options for creating nodes
18
19
  * @returns Created nodes
19
20
  */
20
- (
21
- template: HTMLTemplateElement,
22
- sanitization?: boolean | SanitizeOptions,
23
- ): Node[];
21
+ (template: HTMLTemplateElement, options?: HtmlOptions): Node[];
24
22
 
25
23
  /**
26
24
  * Clear cache of template elements
@@ -34,19 +32,77 @@ type Html = {
34
32
  remove(template: string): void;
35
33
  };
36
34
 
35
+ type HtmlOptions = {
36
+ /**
37
+ * Ignore caching the template element for the HTML string? _(defaults to `false`)_
38
+ */
39
+ ignoreCache?: boolean;
40
+ } & SanitizeOptions;
41
+
42
+ type Options = Required<HtmlOptions>;
43
+
37
44
  //
38
45
 
39
- function createTemplate(html: string): HTMLTemplateElement {
46
+ function createTemplate(html: string, ignore: boolean): HTMLTemplateElement {
40
47
  const template = document.createElement('template');
41
48
 
42
49
  template.innerHTML = html;
43
50
 
44
- templates[html] = template;
51
+ if (!ignore) {
52
+ templates[html] = template;
53
+ }
45
54
 
46
55
  return template;
47
56
  }
48
57
 
49
- function getTemplate(value: string): HTMLTemplateElement | undefined {
58
+ function getHtml(
59
+ value: string | HTMLTemplateElement,
60
+ options: Options,
61
+ ): Node[] {
62
+ if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
63
+ return [];
64
+ }
65
+
66
+ const template =
67
+ value instanceof HTMLTemplateElement
68
+ ? value
69
+ : getTemplate(value, options.ignoreCache);
70
+
71
+ if (template == null) {
72
+ return [];
73
+ }
74
+
75
+ const cloned = template.content.cloneNode(true) as DocumentFragment;
76
+
77
+ const scripts = cloned.querySelectorAll('script');
78
+
79
+ for (const script of scripts) {
80
+ script.remove();
81
+ }
82
+
83
+ cloned.normalize();
84
+
85
+ return sanitizeNodes([...cloned.childNodes], options);
86
+ }
87
+
88
+ function getOptions(input?: HtmlOptions): Options {
89
+ const options = isPlainObject(input) ? input : {};
90
+
91
+ options.ignoreCache =
92
+ typeof options.ignoreCache === 'boolean' ? options.ignoreCache : false;
93
+
94
+ options.sanitizeBooleanAttributes =
95
+ typeof options.sanitizeBooleanAttributes === 'boolean'
96
+ ? options.sanitizeBooleanAttributes
97
+ : true;
98
+
99
+ return options as Options;
100
+ }
101
+
102
+ function getTemplate(
103
+ value: string,
104
+ ignore: boolean,
105
+ ): HTMLTemplateElement | undefined {
50
106
  if (typeof value !== 'string' || value.trim().length === 0) {
51
107
  return;
52
108
  }
@@ -62,66 +118,50 @@ function getTemplate(value: string): HTMLTemplateElement | undefined {
62
118
  : null;
63
119
 
64
120
  template =
65
- element instanceof HTMLTemplateElement ? element : createTemplate(value);
66
-
67
- templates[value] = template;
121
+ element instanceof HTMLTemplateElement
122
+ ? element
123
+ : createTemplate(value, ignore);
68
124
 
69
125
  return template;
70
126
  }
71
127
 
72
128
  const html = ((
73
129
  value: string | HTMLTemplateElement,
74
- sanitization?: boolean | SanitizeOptions,
130
+ options?: Options,
75
131
  ): Node[] => {
76
- if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
77
- return [];
78
- }
132
+ return getHtml(value, getOptions(options));
133
+ }) as Html;
79
134
 
80
- let options: SanitizeOptions | undefined;
135
+ html.clear = (): void => {
136
+ templates = {};
137
+ };
81
138
 
82
- if (sanitization == null || sanitization === true) {
83
- options = {};
84
- } else {
85
- options = sanitization === false ? undefined : sanitization;
139
+ html.remove = (template: string): void => {
140
+ if (typeof template !== 'string' || templates[template] == null) {
141
+ return;
86
142
  }
87
143
 
88
- const template =
89
- value instanceof HTMLTemplateElement ? value : getTemplate(value);
144
+ const keys = Object.keys(templates);
145
+ const {length} = keys;
90
146
 
91
- if (template == null) {
92
- return [];
93
- }
94
-
95
- const cloned = template.content.cloneNode(true) as DocumentFragment;
96
- const scripts = cloned.querySelectorAll('script');
97
- const {length} = scripts;
147
+ const updated: Record<string, HTMLTemplateElement> = {};
98
148
 
99
149
  for (let index = 0; index < length; index += 1) {
100
- scripts[index].remove();
101
- }
102
-
103
- cloned.normalize();
104
-
105
- return options != null
106
- ? sanitize([...cloned.childNodes], options)
107
- : [...cloned.childNodes];
108
- }) as Html;
150
+ const key = keys[index];
109
151
 
110
- html.clear = (): void => {
111
- templates = {};
112
- };
113
-
114
- html.remove = (template: string): void => {
115
- if (typeof template === 'string') {
116
- templates[template] = undefined;
152
+ if (key !== template) {
153
+ updated[key] = templates[key];
154
+ }
117
155
  }
156
+
157
+ templates = updated;
118
158
  };
119
159
 
120
160
  //
121
161
 
122
162
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
123
163
 
124
- let templates: Record<string, HTMLTemplateElement | undefined> = {};
164
+ let templates: Record<string, HTMLTemplateElement> = {};
125
165
 
126
166
  //
127
167
 
@@ -0,0 +1,78 @@
1
+ import {isPlainObject} from '@oscarpalmer/atoms/is';
2
+ import {
3
+ isBadAttribute,
4
+ isEmptyNonBooleanAttribute,
5
+ isInvalidBooleanAttribute,
6
+ } from '../attribute';
7
+
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 {
39
+ const {length} = attributes;
40
+
41
+ for (let index = 0; index < length; index += 1) {
42
+ const attribute = attributes[index];
43
+
44
+ if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) {
45
+ element.removeAttribute(attribute.name);
46
+ } else if (
47
+ options.sanitizeBooleanAttributes &&
48
+ isInvalidBooleanAttribute(attribute)
49
+ ) {
50
+ element.setAttribute(attribute.name, '');
51
+ }
52
+ }
53
+ }
54
+
55
+ export function sanitizeNodes(nodes: Node[], options: Options): Node[] {
56
+ const actual = nodes.filter(node => node instanceof Node);
57
+ const {length} = nodes;
58
+
59
+ for (let index = 0; index < length; index += 1) {
60
+ const node = actual[index];
61
+
62
+ if (node instanceof Element) {
63
+ const scripts = node.querySelectorAll('script');
64
+
65
+ for (const script of scripts) {
66
+ script.remove();
67
+ }
68
+
69
+ sanitizeAttributes(node, [...node.attributes], options);
70
+ }
71
+
72
+ if (node.hasChildNodes()) {
73
+ sanitizeNodes([...node.childNodes], options);
74
+ }
75
+ }
76
+
77
+ return nodes;
78
+ }
package/src/sanitize.ts CHANGED
@@ -1,30 +1,8 @@
1
- import {isPlainObject} from '@oscarpalmer/atoms/is';
2
1
  import {
3
- isBadAttribute,
4
- isEmptyNonBooleanAttribute,
5
- isInvalidBooleanAttribute,
6
- } from './attribute';
7
-
8
- type Options = Required<SanitizeOptions>;
9
-
10
- export type SanitizeOptions = {
11
- /**
12
- * - Sanitize boolean attributes? _(Defaults to `true`)_
13
- * - E.g. `checked="abc"` => `checked=""`
14
- */
15
- sanitizeBooleanAttributes?: boolean;
16
- };
17
-
18
- function getOptions(input?: SanitizeOptions): Options {
19
- const options = isPlainObject(input) ? input : {};
20
-
21
- options.sanitizeBooleanAttributes =
22
- typeof options.sanitizeBooleanAttributes === 'boolean'
23
- ? options.sanitizeBooleanAttributes
24
- : true;
25
-
26
- return options as Options;
27
- }
2
+ getSanitizeOptions,
3
+ type SanitizeOptions,
4
+ sanitizeNodes,
5
+ } from './internal/sanitize';
28
6
 
29
7
  /**
30
8
  * Sanitize one or more nodes, recursively
@@ -34,50 +12,10 @@ function getOptions(input?: SanitizeOptions): Options {
34
12
  */
35
13
  export function sanitize(
36
14
  value: Node | Node[],
37
- options?: Partial<SanitizeOptions>,
15
+ options?: SanitizeOptions,
38
16
  ): Node[] {
39
17
  return sanitizeNodes(
40
18
  Array.isArray(value) ? value : [value],
41
- getOptions(options),
19
+ getSanitizeOptions(options),
42
20
  );
43
21
  }
44
-
45
- function sanitizeAttributes(
46
- element: Element,
47
- attributes: Attr[],
48
- options: SanitizeOptions,
49
- ): void {
50
- const {length} = attributes;
51
-
52
- for (let index = 0; index < length; index += 1) {
53
- const attribute = attributes[index];
54
-
55
- if (isBadAttribute(attribute) || isEmptyNonBooleanAttribute(attribute)) {
56
- element.removeAttribute(attribute.name);
57
- } else if (
58
- options.sanitizeBooleanAttributes &&
59
- isInvalidBooleanAttribute(attribute)
60
- ) {
61
- element.setAttribute(attribute.name, '');
62
- }
63
- }
64
- }
65
-
66
- function sanitizeNodes(nodes: Node[], options: SanitizeOptions): Node[] {
67
- const actual = nodes.filter(node => node instanceof Node);
68
- const {length} = nodes;
69
-
70
- for (let index = 0; index < length; index += 1) {
71
- const node = actual[index];
72
-
73
- if (node instanceof Element) {
74
- sanitizeAttributes(node, [...node.attributes], options);
75
- }
76
-
77
- if (node.hasChildNodes()) {
78
- sanitizeNodes([...node.childNodes], options);
79
- }
80
- }
81
-
82
- return nodes;
83
- }
package/types/html.d.ts CHANGED
@@ -1,19 +1,19 @@
1
- import { type SanitizeOptions } from './sanitize';
1
+ import { type SanitizeOptions } from './internal/sanitize';
2
2
  type Html = {
3
3
  /**
4
4
  * Create nodes from an HTML string or a template element
5
5
  * @param value HTML string or id for a template element
6
- * @param sanitization Sanitization options
6
+ * @param options Options for creating nodes
7
7
  * @returns Created nodes
8
8
  */
9
- (value: string, sanitization?: boolean | SanitizeOptions): Node[];
9
+ (value: string, options?: HtmlOptions): Node[];
10
10
  /**
11
11
  * Create nodes from a template element
12
12
  * @param template Template element
13
- * @param sanitization Sanitization options
13
+ * @param options Options for creating nodes
14
14
  * @returns Created nodes
15
15
  */
16
- (template: HTMLTemplateElement, sanitization?: boolean | SanitizeOptions): Node[];
16
+ (template: HTMLTemplateElement, options?: HtmlOptions): Node[];
17
17
  /**
18
18
  * Clear cache of template elements
19
19
  */
@@ -24,5 +24,11 @@ type Html = {
24
24
  */
25
25
  remove(template: string): void;
26
26
  };
27
+ type HtmlOptions = {
28
+ /**
29
+ * Ignore caching the template element for the HTML string? _(defaults to `false`)_
30
+ */
31
+ ignoreCache?: boolean;
32
+ } & SanitizeOptions;
27
33
  declare const html: Html;
28
34
  export { html };
@@ -0,0 +1,13 @@
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,14 +1,8 @@
1
- export type SanitizeOptions = {
2
- /**
3
- * - Sanitize boolean attributes? _(Defaults to `true`)_
4
- * - E.g. `checked="abc"` => `checked=""`
5
- */
6
- sanitizeBooleanAttributes?: boolean;
7
- };
1
+ import { type SanitizeOptions } from './internal/sanitize';
8
2
  /**
9
3
  * Sanitize one or more nodes, recursively
10
4
  * @param value Node or nodes to sanitize
11
5
  * @param options Sanitization options
12
6
  * @returns Sanitized nodes
13
7
  */
14
- export declare function sanitize(value: Node | Node[], options?: Partial<SanitizeOptions>): Node[];
8
+ export declare function sanitize(value: Node | Node[], options?: SanitizeOptions): Node[];