@prairielearn/html 3.1.7 → 4.0.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/.mocharc.cjs +3 -0
- package/CHANGELOG.md +6 -0
- package/README.md +10 -10
- package/dist/index.js +5 -13
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +17 -19
- package/dist/index.test.js.map +1 -1
- package/package.json +4 -3
- package/src/index.test.ts +1 -1
package/.mocharc.cjs
ADDED
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -6,21 +6,21 @@ Utilities for easily rendering HTML from within JavaScript.
|
|
|
6
6
|
|
|
7
7
|
The `html` tagged template literal can be used to render HTML while ensuring that any interpolated values are properly escaped.
|
|
8
8
|
|
|
9
|
-
By convention, HTML templates are located in `*.html.
|
|
9
|
+
By convention, HTML templates are located in `*.html.ts` files.
|
|
10
10
|
|
|
11
|
-
```
|
|
12
|
-
// Hello.html.
|
|
13
|
-
|
|
11
|
+
```ts
|
|
12
|
+
// Hello.html.ts
|
|
13
|
+
import { html } from '@prairielearn/html';
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
export function Hello({ name }: { name: string }) {
|
|
16
16
|
return html`<div>Hello, ${name}!</div>`;
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
This can then be used to render a string:
|
|
21
21
|
|
|
22
|
-
```
|
|
23
|
-
|
|
22
|
+
```ts
|
|
23
|
+
import { Hello } from './Hello.html.ts';
|
|
24
24
|
|
|
25
25
|
console.log(Hello({ name: 'Anjali' }).toString());
|
|
26
26
|
// Prints "<div>Hello, Anjali!</div>"
|
|
@@ -30,8 +30,8 @@ console.log(Hello({ name: 'Anjali' }).toString());
|
|
|
30
30
|
|
|
31
31
|
If you want to pre-escape some HTML, you can wrap it in `escapeHtml` to avoid escaping it twice. This is useful if you want to inline some HTML into an attribute, for instance with a Bootstrap popover.
|
|
32
32
|
|
|
33
|
-
```
|
|
34
|
-
|
|
33
|
+
```ts
|
|
34
|
+
import { html, escapeHtml } from '@prairielearn/html';
|
|
35
35
|
|
|
36
36
|
console.log(html`
|
|
37
37
|
<button data-bs-toggle="popover" data-bs-content="${escapeHtml(html`<div>Content here</div>`)}">
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.joinHtml = exports.unsafeHtml = exports.escapeHtml = exports.html = exports.HtmlSafeString = void 0;
|
|
4
1
|
const ENCODE_HTML_RULES = {
|
|
5
2
|
'&': '&',
|
|
6
3
|
'<': '<',
|
|
@@ -45,7 +42,7 @@ function escapeValue(value) {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
// Based on https://github.com/Janpot/escape-html-template-tag
|
|
48
|
-
class HtmlSafeString {
|
|
45
|
+
export class HtmlSafeString {
|
|
49
46
|
strings;
|
|
50
47
|
values;
|
|
51
48
|
constructor(strings, values) {
|
|
@@ -58,20 +55,17 @@ class HtmlSafeString {
|
|
|
58
55
|
}, this.strings[0]);
|
|
59
56
|
}
|
|
60
57
|
}
|
|
61
|
-
|
|
62
|
-
function html(strings, ...values) {
|
|
58
|
+
export function html(strings, ...values) {
|
|
63
59
|
return new HtmlSafeString(strings, values);
|
|
64
60
|
}
|
|
65
|
-
exports.html = html;
|
|
66
61
|
/**
|
|
67
62
|
* Pre-escapes the rendered HTML. Useful for when you want to inline the HTML
|
|
68
63
|
* in something else, for instance in a `data-content` attribute for a Bootstrap
|
|
69
64
|
* popover.
|
|
70
65
|
*/
|
|
71
|
-
function escapeHtml(html) {
|
|
66
|
+
export function escapeHtml(html) {
|
|
72
67
|
return unsafeHtml(escapeHtmlRaw(html.toString()));
|
|
73
68
|
}
|
|
74
|
-
exports.escapeHtml = escapeHtml;
|
|
75
69
|
/**
|
|
76
70
|
* Will render the provided value without any additional escaping. Use carefully
|
|
77
71
|
* with user-provided data.
|
|
@@ -79,18 +73,16 @@ exports.escapeHtml = escapeHtml;
|
|
|
79
73
|
* @param value The value to render.
|
|
80
74
|
* @returns An {@link HtmlSafeString} representing the provided value.
|
|
81
75
|
*/
|
|
82
|
-
function unsafeHtml(value) {
|
|
76
|
+
export function unsafeHtml(value) {
|
|
83
77
|
return new HtmlSafeString([value], []);
|
|
84
78
|
}
|
|
85
|
-
exports.unsafeHtml = unsafeHtml;
|
|
86
79
|
/**
|
|
87
80
|
* Joins a list of HTML values with a separator.
|
|
88
81
|
*
|
|
89
82
|
* @param values The values to join.
|
|
90
83
|
* @param separator The separator to use between values.
|
|
91
84
|
*/
|
|
92
|
-
function joinHtml(values, separator = '') {
|
|
85
|
+
export function joinHtml(values, separator = '') {
|
|
93
86
|
return unsafeHtml(values.map(escapeValue).join(escapeValue(separator)));
|
|
94
87
|
}
|
|
95
|
-
exports.joinHtml = joinHtml;
|
|
96
88
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["const ENCODE_HTML_RULES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\nconst MATCH_HTML = /[&<>'\"]/g;\n\nfunction encodeCharacter(c: string) {\n return ENCODE_HTML_RULES[c] || c;\n}\n\n/**\n * Based on the `escapeXML` function from the `ejs` library.\n */\nfunction escapeHtmlRaw(value: string): string {\n return value == null ? '' : String(value).replace(MATCH_HTML, encodeCharacter);\n}\n\nfunction escapeValue(value: unknown): string {\n if (value instanceof HtmlSafeString) {\n // Already escaped!\n return value.toString();\n } else if (Array.isArray(value)) {\n return value.map((val) => escapeValue(val)).join('');\n } else if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return escapeHtmlRaw(String(value));\n } else if (value == null) {\n // undefined or null -- render nothing\n return '';\n } else if (typeof value === 'object') {\n throw new Error(`Cannot interpolate object in template: ${JSON.stringify(value)}`);\n } else {\n // There shouldn't be any other types\n throw new Error(\n `Unexpected type in template: ${typeof value} for value ${JSON.stringify(value)}`,\n );\n }\n}\n\n// Based on https://github.com/Janpot/escape-html-template-tag\nexport class HtmlSafeString {\n private readonly strings: readonly string[];\n private readonly values: unknown[];\n\n constructor(strings: readonly string[], values: unknown[]) {\n this.strings = strings;\n this.values = values;\n }\n\n toString(): string {\n return this.values.reduce<string>((acc, val, i) => {\n return acc + escapeValue(val) + this.strings[i + 1];\n }, this.strings[0]);\n }\n}\n\nexport type HtmlValue =\n | string\n | number\n | boolean\n | bigint\n | HtmlSafeString\n | undefined\n | null\n | HtmlValue[];\n\nexport function html(strings: TemplateStringsArray, ...values: HtmlValue[]): HtmlSafeString {\n return new HtmlSafeString(strings, values);\n}\n\n/**\n * Pre-escapes the rendered HTML. Useful for when you want to inline the HTML\n * in something else, for instance in a `data-content` attribute for a Bootstrap\n * popover.\n */\nexport function escapeHtml(html: HtmlSafeString): HtmlSafeString {\n return unsafeHtml(escapeHtmlRaw(html.toString()));\n}\n\n/**\n * Will render the provided value without any additional escaping. Use carefully\n * with user-provided data.\n *\n * @param value The value to render.\n * @returns An {@link HtmlSafeString} representing the provided value.\n */\nexport function unsafeHtml(value: string): HtmlSafeString {\n return new HtmlSafeString([value], []);\n}\n\n/**\n * Joins a list of HTML values with a separator.\n *\n * @param values The values to join.\n * @param separator The separator to use between values.\n */\nexport function joinHtml(values: HtmlValue[], separator: HtmlValue = ''): HtmlSafeString {\n return unsafeHtml(values.map(escapeValue).join(escapeValue(separator)));\n}\n"]}
|
package/dist/index.test.js
CHANGED
|
@@ -1,51 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const index_1 = require("./index");
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
import { escapeHtml, html } from './index.js';
|
|
5
3
|
describe('html', () => {
|
|
6
4
|
it('escapes string value', () => {
|
|
7
|
-
|
|
5
|
+
assert.equal(html `<p>${'<script>'}</p>`.toString(), '<p><script></p>');
|
|
8
6
|
});
|
|
9
7
|
it('interpolates multiple values', () => {
|
|
10
|
-
|
|
8
|
+
assert.equal(html `<p>${'cats'} and ${'dogs'}</p>`.toString(), '<p>cats and dogs</p>');
|
|
11
9
|
});
|
|
12
10
|
it('interpolates a number', () => {
|
|
13
|
-
|
|
11
|
+
assert.equal(html `<p>${123}</p>`.toString(), '<p>123</p>');
|
|
14
12
|
});
|
|
15
13
|
it('interpolates a bigint', () => {
|
|
16
|
-
|
|
14
|
+
assert.equal(html `<p>${123n}</p>`.toString(), '<p>123</p>');
|
|
17
15
|
});
|
|
18
16
|
it('interpolates a boolean', () => {
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
assert.equal(html `<p>${true}</p>`.toString(), '<p>true</p>');
|
|
18
|
+
assert.equal(html `<p>${false}</p>`.toString(), '<p>false</p>');
|
|
21
19
|
});
|
|
22
20
|
it('escapes values when rendering array', () => {
|
|
23
21
|
const arr = ['cats>', '<dogs'];
|
|
24
|
-
|
|
22
|
+
assert.equal(
|
|
25
23
|
// prettier-ignore
|
|
26
|
-
|
|
24
|
+
html `<ul>${arr}</ul>`.toString(), '<ul>cats><dogs</ul>');
|
|
27
25
|
});
|
|
28
26
|
it('does not double-escape values when rendering array', () => {
|
|
29
27
|
const arr = ['cats', 'dogs'];
|
|
30
|
-
|
|
28
|
+
assert.equal(
|
|
31
29
|
// prettier-ignore
|
|
32
|
-
|
|
30
|
+
html `<ul>${arr.map((e) => html `<li>${e}</li>`)}</ul>`.toString(), '<ul><li>cats</li><li>dogs</li></ul>');
|
|
33
31
|
});
|
|
34
32
|
it('errors when interpolating object', () => {
|
|
35
|
-
|
|
33
|
+
assert.throws(
|
|
36
34
|
// @ts-expect-error -- Testing runtime behavior of bad input.
|
|
37
|
-
() =>
|
|
35
|
+
() => html `<p>${{ foo: 'bar' }}</p>`.toString(), 'Cannot interpolate object in template');
|
|
38
36
|
});
|
|
39
37
|
it('omits nullish values from template', () => {
|
|
40
|
-
|
|
38
|
+
assert.equal(html `<p>${null}${undefined}</p>`.toString(), '<p></p>');
|
|
41
39
|
});
|
|
42
40
|
});
|
|
43
41
|
describe('escapeHtml', () => {
|
|
44
42
|
it('escapes rendered HTML', () => {
|
|
45
|
-
|
|
43
|
+
assert.equal(escapeHtml(html `<p>Hello</p>`).toString(), '<p>Hello</p>');
|
|
46
44
|
});
|
|
47
45
|
it('works when nested inside html tag', () => {
|
|
48
|
-
|
|
46
|
+
assert.equal(html `a${escapeHtml(html `<p></p>`)}b`.toString(), 'a<p></p>b');
|
|
49
47
|
});
|
|
50
48
|
});
|
|
51
49
|
//# sourceMappingURL=index.test.js.map
|
package/dist/index.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from 'chai';\n\nimport { escapeHtml, html } from './index.js';\n\ndescribe('html', () => {\n it('escapes string value', () => {\n assert.equal(html`<p>${'<script>'}</p>`.toString(), '<p><script></p>');\n });\n\n it('interpolates multiple values', () => {\n assert.equal(html`<p>${'cats'} and ${'dogs'}</p>`.toString(), '<p>cats and dogs</p>');\n });\n\n it('interpolates a number', () => {\n assert.equal(html`<p>${123}</p>`.toString(), '<p>123</p>');\n });\n\n it('interpolates a bigint', () => {\n assert.equal(html`<p>${123n}</p>`.toString(), '<p>123</p>');\n });\n\n it('interpolates a boolean', () => {\n assert.equal(html`<p>${true}</p>`.toString(), '<p>true</p>');\n assert.equal(html`<p>${false}</p>`.toString(), '<p>false</p>');\n });\n\n it('escapes values when rendering array', () => {\n const arr = ['cats>', '<dogs'];\n assert.equal(\n // prettier-ignore\n html`<ul>${arr}</ul>`.toString(),\n '<ul>cats><dogs</ul>',\n );\n });\n\n it('does not double-escape values when rendering array', () => {\n const arr = ['cats', 'dogs'];\n assert.equal(\n // prettier-ignore\n html`<ul>${arr.map((e) => html`<li>${e}</li>`)}</ul>`.toString(),\n '<ul><li>cats</li><li>dogs</li></ul>',\n );\n });\n\n it('errors when interpolating object', () => {\n assert.throws(\n // @ts-expect-error -- Testing runtime behavior of bad input.\n () => html`<p>${{ foo: 'bar' }}</p>`.toString(),\n 'Cannot interpolate object in template',\n );\n });\n\n it('omits nullish values from template', () => {\n assert.equal(html`<p>${null}${undefined}</p>`.toString(), '<p></p>');\n });\n});\n\ndescribe('escapeHtml', () => {\n it('escapes rendered HTML', () => {\n assert.equal(escapeHtml(html`<p>Hello</p>`).toString(), '<p>Hello</p>');\n });\n\n it('works when nested inside html tag', () => {\n assert.equal(html`a${escapeHtml(html`<p></p>`)}b`.toString(), 'a<p></p>b');\n });\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/html",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"main": "dist/index.js",
|
|
5
6
|
"sideEffects": false,
|
|
6
7
|
"repository": {
|
|
@@ -11,14 +12,14 @@
|
|
|
11
12
|
"scripts": {
|
|
12
13
|
"build": "tsc",
|
|
13
14
|
"dev": "tsc --watch --preserveWatchOutput",
|
|
14
|
-
"test": "mocha
|
|
15
|
+
"test": "mocha src/**/*.test.ts"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
17
18
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
18
19
|
"@types/node": "^20.12.2",
|
|
19
20
|
"chai": "^4.4.1",
|
|
20
21
|
"mocha": "^10.4.0",
|
|
21
|
-
"tsx": "^4.
|
|
22
|
+
"tsx": "^4.9.3",
|
|
22
23
|
"typescript": "^5.4.3"
|
|
23
24
|
}
|
|
24
25
|
}
|