@prairielearn/html 1.0.2 → 2.1.1

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.
@@ -1,3 +1,3 @@
1
- @prairielearn/html:build: cache hit, replaying output 5c9396a1c44d5368
1
+ @prairielearn/html:build: cache hit, replaying output b42467e94478b467
2
2
  @prairielearn/html:build: warning package.json: No license field
3
3
  @prairielearn/html:build: $ tsc
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @prairielearn/html
2
2
 
3
+ ## 2.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - dba390399: Upgrade dependencies to latest versions
8
+
9
+ ## 2.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - c23684a26: Type constraints for interpolated values
14
+
15
+ ## 2.0.0
16
+
17
+ ### Major Changes
18
+
19
+ - 56f1333fc: `renderEjs` function moved to `@prairielearn/html-ejs`
20
+
21
+ In order to be able to use the `@prairielearn/html` package inside client scripts, EJS functionality was moved to a separate package (`@prairielearn/html-ejs`). The `ejs` package relies on Node-only packages like `fs` and `path`, which renders it unusable in browsers.
22
+
3
23
  ## 1.0.2
4
24
 
5
25
  ### Patch Changes
package/README.md CHANGED
@@ -40,26 +40,6 @@ console.log(html`
40
40
  `);
41
41
  ```
42
42
 
43
- ### Using with EJS
44
-
45
- If you have an EJS partial that you'd like to use inside of an `html` tagged template literal, you can use the `renderEjs` helper:
46
-
47
- ```html
48
- <!-- hello.ejs -->
49
- Hello, <%= name %>!
50
- ```
51
-
52
- ```js
53
- const { hmtl, renderEjs } = require('@prairielearn/html');
54
-
55
- console.log(
56
- html`
57
- <div>Hello, world!</div>
58
- <div>${renderEjs(__filename, "<%- include('./hello'); %>", { name: 'Anjali' })}</div>
59
- `.toString()
60
- );
61
- ```
62
-
63
43
  ## Why not EJS?
64
44
 
65
45
  PrairieLearn used (and still uses) EJS to render most views. However, using a tagged template literal and pure JavaScript to render views has a number of advantages:
@@ -67,3 +47,5 @@ PrairieLearn used (and still uses) EJS to render most views. However, using a ta
67
47
  - Prettier will automatically format the content of any `html` tagged template literal; EJS does not have any automatic formatters.
68
48
  - Authoring views in pure JavaScript allows for easier and more explicit composition of components.
69
49
  - It's possible to use ESLint and TypeScript to type-check JavaScript views; EJS does not offer support for either.
50
+
51
+ If you want to use existing EJS partials inside of an `html` tagged template literal, check out the `@prairielearn/html-ejs` package. EJS-related functionality is deliberately located in a separate package so that `@prairielearn/html` can be used in the browser, since the `ejs` package makes use of Node-only features.
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ export declare class HtmlSafeString {
4
4
  constructor(strings: ReadonlyArray<string>, values: unknown[]);
5
5
  toString(): string;
6
6
  }
7
- export declare function html(strings: TemplateStringsArray, ...values: any[]): HtmlSafeString;
7
+ export declare type HtmlValue = string | number | boolean | HtmlSafeString | undefined | null | HtmlValue[];
8
+ export declare function html(strings: TemplateStringsArray, ...values: HtmlValue[]): HtmlSafeString;
8
9
  /**
9
10
  * Pre-escpapes the rendered HTML. Useful for when you want to inline the HTML
10
11
  * in something else, for instance in a `data-content` attribute for a Bootstrap
@@ -19,16 +20,3 @@ export declare function escapeHtml(html: HtmlSafeString): HtmlSafeString;
19
20
  * @returns An {@link HtmlSafeString} representing the provided value.
20
21
  */
21
22
  export declare function unsafeHtml(value: string): HtmlSafeString;
22
- /**
23
- * This is a shim to allow for the use of EJS templates inside of HTML tagged
24
- * template literals.
25
- *
26
- * The resulting string is assumed to be appropriately escaped and will be used
27
- * verbatim in the resulting HTML.
28
- *
29
- * @param filename The name of the file from which relative includes should be resolved.
30
- * @param template The raw EJS template string.
31
- * @param data Any data to be made available to the template.
32
- * @returns The rendered EJS.
33
- */
34
- export declare function renderEjs(filename: string, template: string, data?: any): HtmlSafeString;
package/dist/index.js CHANGED
@@ -1,11 +1,23 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderEjs = exports.unsafeHtml = exports.escapeHtml = exports.html = exports.HtmlSafeString = void 0;
7
- const ejs_1 = __importDefault(require("ejs"));
8
- const path_1 = __importDefault(require("path"));
3
+ exports.unsafeHtml = exports.escapeHtml = exports.html = exports.HtmlSafeString = void 0;
4
+ const ENCODE_HTML_RULES = {
5
+ '&': '&amp;',
6
+ '<': '&lt;',
7
+ '>': '&gt;',
8
+ '"': '&#34;',
9
+ "'": '&#39;',
10
+ };
11
+ var MATCH_HTML = /[&<>'"]/g;
12
+ function encodeCharacter(c) {
13
+ return ENCODE_HTML_RULES[c] || c;
14
+ }
15
+ /**
16
+ * Based on the `escapeXML` function from the `ejs` library.
17
+ */
18
+ function escapeHtmlRaw(value) {
19
+ return value == undefined ? '' : String(value).replace(MATCH_HTML, encodeCharacter);
20
+ }
9
21
  function escapeValue(value) {
10
22
  if (value instanceof HtmlSafeString) {
11
23
  // Already escaped!
@@ -15,7 +27,7 @@ function escapeValue(value) {
15
27
  return value.map((val) => escapeValue(val)).join('');
16
28
  }
17
29
  else if (typeof value === 'string' || typeof value === 'number') {
18
- return ejs_1.default.escapeXML(String(value));
30
+ return escapeHtmlRaw(String(value));
19
31
  }
20
32
  else if (value == null) {
21
33
  // undefined or null -- render nothing
@@ -52,7 +64,7 @@ exports.html = html;
52
64
  * popover.
53
65
  */
54
66
  function escapeHtml(html) {
55
- return unsafeHtml(ejs_1.default.escapeXML(html.toString()));
67
+ return unsafeHtml(escapeHtmlRaw(html.toString()));
56
68
  }
57
69
  exports.escapeHtml = escapeHtml;
58
70
  /**
@@ -66,20 +78,4 @@ function unsafeHtml(value) {
66
78
  return new HtmlSafeString([value], []);
67
79
  }
68
80
  exports.unsafeHtml = unsafeHtml;
69
- /**
70
- * This is a shim to allow for the use of EJS templates inside of HTML tagged
71
- * template literals.
72
- *
73
- * The resulting string is assumed to be appropriately escaped and will be used
74
- * verbatim in the resulting HTML.
75
- *
76
- * @param filename The name of the file from which relative includes should be resolved.
77
- * @param template The raw EJS template string.
78
- * @param data Any data to be made available to the template.
79
- * @returns The rendered EJS.
80
- */
81
- function renderEjs(filename, template, data = {}) {
82
- return unsafeHtml(ejs_1.default.render(template, data, { views: [path_1.default.dirname(filename)] }));
83
- }
84
- exports.renderEjs = renderEjs;
85
81
  //# 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":";;;;;;AAAA,8CAAsB;AACtB,gDAAwB;AAExB,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,YAAY,cAAc,EAAE;QACnC,mBAAmB;QACnB,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;KACzB;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QAC/B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACtD;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACjE,OAAO,aAAG,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;KACrC;SAAM,IAAI,KAAK,IAAI,IAAI,EAAE;QACxB,sCAAsC;QACtC,OAAO,EAAE,CAAC;KACX;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACpC,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;KACpF;SAAM;QACL,gDAAgD;QAChD,OAAO,EAAE,CAAC;KACX;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAa,cAAc;IAIzB,YAAY,OAA8B,EAAE,MAAiB;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAS,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;YAChD,OAAO,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;CACF;AAdD,wCAcC;AAED,SAAgB,IAAI,CAAC,OAA6B,EAAE,GAAG,MAAa;IAClE,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAFD,oBAEC;AAED;;;;GAIG;AACH,SAAgB,UAAU,CAAC,IAAoB;IAC7C,OAAO,UAAU,CAAC,aAAG,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC;AAFD,gCAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,cAAc,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAFD,gCAEC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,SAAS,CAAC,QAAgB,EAAE,QAAgB,EAAE,OAAY,EAAE;IAC1E,OAAO,UAAU,CAAC,aAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACrF,CAAC;AAFD,8BAEC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,MAAM,iBAAiB,GAAG;IACxB,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,OAAO;CACb,CAAC;AACF,IAAI,UAAU,GAAG,UAAU,CAAC;AAE5B,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AACtF,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,YAAY,cAAc,EAAE;QACnC,mBAAmB;QACnB,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;KACzB;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QAC/B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACtD;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACjE,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;KACrC;SAAM,IAAI,KAAK,IAAI,IAAI,EAAE;QACxB,sCAAsC;QACtC,OAAO,EAAE,CAAC;KACX;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACpC,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;KACpF;SAAM;QACL,gDAAgD;QAChD,OAAO,EAAE,CAAC;KACX;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAa,cAAc;IAIzB,YAAY,OAA8B,EAAE,MAAiB;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAS,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;YAChD,OAAO,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;CACF;AAdD,wCAcC;AAID,SAAgB,IAAI,CAAC,OAA6B,EAAE,GAAG,MAAmB;IACxE,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAFD,oBAEC;AAED;;;;GAIG;AACH,SAAgB,UAAU,CAAC,IAAoB;IAC7C,OAAO,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC;AAFD,gCAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,cAAc,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAFD,gCAEC"}
@@ -22,7 +22,9 @@ describe('html', () => {
22
22
  (0, index_1.html) `<ul>${arr.map((e) => (0, index_1.html) `<li>${e}</li>`)}</ul>`.toString(), '<ul><li>cats</li><li>dogs</li></ul>');
23
23
  });
24
24
  it('errors when interpolating object', () => {
25
- chai_1.assert.throws(() => (0, index_1.html) `<p>${{ foo: 'bar' }}</p>`.toString(), 'Cannot interpolate object in template');
25
+ chai_1.assert.throws(
26
+ // @ts-expect-error
27
+ () => (0, index_1.html) `<p>${{ foo: 'bar' }}</p>`.toString(), 'Cannot interpolate object in template');
26
28
  });
27
29
  it('omits boolean values from template', () => {
28
30
  chai_1.assert.equal((0, index_1.html) `<p>${true}${false}</p>`.toString(), '<p></p>');
@@ -39,12 +41,4 @@ describe('escapeHtml', () => {
39
41
  chai_1.assert.equal((0, index_1.html) `a${(0, index_1.escapeHtml)((0, index_1.html) `<p></p>`)}b`.toString(), 'a&lt;p&gt;&lt;/p&gt;b');
40
42
  });
41
43
  });
42
- describe('renderEjs', () => {
43
- it('renders EJS template without data', () => {
44
- chai_1.assert.equal((0, index_1.renderEjs)(__filename, '<p>Hello</p>', {}).toString(), '<p>Hello</p>');
45
- });
46
- it('renders EJS template with data', () => {
47
- chai_1.assert.equal((0, index_1.renderEjs)(__filename, '<p>Hello <%= name %></p>', { name: 'Divya' }).toString(), '<p>Hello Divya</p>');
48
- });
49
- });
50
44
  //# sourceMappingURL=index.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":";;AAAA,+BAA8B;AAE9B,mCAA2C;AAE3C,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,aAAM,CAAC,KAAK,CAAC,IAAA,YAAI,EAAA,MAAM,UAAU,MAAM,CAAC,QAAQ,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,aAAM,CAAC,KAAK,CAAC,IAAA,YAAI,EAAA,MAAM,MAAM,QAAQ,MAAM,MAAM,CAAC,QAAQ,EAAE,EAAE,sBAAsB,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/B,aAAM,CAAC,KAAK;QACV,kBAAkB;QAClB,IAAA,YAAI,EAAA,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,EAChC,2BAA2B,CAC5B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7B,aAAM,CAAC,KAAK;QACV,kBAAkB;QAClB,IAAA,YAAI,EAAA,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,YAAI,EAAA,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,EAChE,qCAAqC,CACtC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,aAAM,CAAC,MAAM;QACX,mBAAmB;QACnB,GAAG,EAAE,CAAC,IAAA,YAAI,EAAA,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,EAC/C,uCAAuC,CACxC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,aAAM,CAAC,KAAK,CAAC,IAAA,YAAI,EAAA,MAAM,IAAI,GAAG,KAAK,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,aAAM,CAAC,KAAK,CAAC,IAAA,YAAI,EAAA,MAAM,IAAI,GAAG,SAAS,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,aAAM,CAAC,KAAK,CAAC,IAAA,kBAAU,EAAC,IAAA,YAAI,EAAA,cAAc,CAAC,CAAC,QAAQ,EAAE,EAAE,0BAA0B,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,aAAM,CAAC,KAAK,CAAC,IAAA,YAAI,EAAA,IAAI,IAAA,kBAAU,EAAC,IAAA,YAAI,EAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,uBAAuB,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "@prairielearn/html",
3
- "version": "1.0.2",
3
+ "version": "2.1.1",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "build": "tsc",
7
7
  "dev": "tsc --watch --preserveWatchOutput",
8
- "test": "mocha --require ts-node/register src/index.test.ts"
9
- },
10
- "dependencies": {
11
- "ejs": "^3.1.6"
8
+ "test": "mocha --no-config --require ts-node/register src/index.test.ts"
12
9
  },
13
10
  "devDependencies": {
14
- "@types/ejs": "^3.1.0",
15
- "mocha": "^9.2.2",
16
- "ts-node": "^10.7.0",
17
- "typescript": "^4.6.3"
11
+ "@prairielearn/tsconfig": "*",
12
+ "mocha": "^10.0.0",
13
+ "ts-node": "^10.9.1",
14
+ "typescript": "^4.8.2"
18
15
  }
19
16
  }
package/src/index.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { assert } from 'chai';
2
2
 
3
- import { escapeHtml, html, renderEjs } from './index';
3
+ import { escapeHtml, html } from './index';
4
4
 
5
5
  describe('html', () => {
6
6
  it('escapes string value', () => {
@@ -31,6 +31,7 @@ describe('html', () => {
31
31
 
32
32
  it('errors when interpolating object', () => {
33
33
  assert.throws(
34
+ // @ts-expect-error
34
35
  () => html`<p>${{ foo: 'bar' }}</p>`.toString(),
35
36
  'Cannot interpolate object in template'
36
37
  );
@@ -54,16 +55,3 @@ describe('escapeHtml', () => {
54
55
  assert.equal(html`a${escapeHtml(html`<p></p>`)}b`.toString(), 'a&lt;p&gt;&lt;/p&gt;b');
55
56
  });
56
57
  });
57
-
58
- describe('renderEjs', () => {
59
- it('renders EJS template without data', () => {
60
- assert.equal(renderEjs(__filename, '<p>Hello</p>', {}).toString(), '<p>Hello</p>');
61
- });
62
-
63
- it('renders EJS template with data', () => {
64
- assert.equal(
65
- renderEjs(__filename, '<p>Hello <%= name %></p>', { name: 'Divya' }).toString(),
66
- '<p>Hello Divya</p>'
67
- );
68
- });
69
- });
package/src/index.ts CHANGED
@@ -1,5 +1,22 @@
1
- import ejs from 'ejs';
2
- import path from 'path';
1
+ const ENCODE_HTML_RULES = {
2
+ '&': '&amp;',
3
+ '<': '&lt;',
4
+ '>': '&gt;',
5
+ '"': '&#34;',
6
+ "'": '&#39;',
7
+ };
8
+ var MATCH_HTML = /[&<>'"]/g;
9
+
10
+ function encodeCharacter(c: string) {
11
+ return ENCODE_HTML_RULES[c] || c;
12
+ }
13
+
14
+ /**
15
+ * Based on the `escapeXML` function from the `ejs` library.
16
+ */
17
+ function escapeHtmlRaw(value: string): string {
18
+ return value == undefined ? '' : String(value).replace(MATCH_HTML, encodeCharacter);
19
+ }
3
20
 
4
21
  function escapeValue(value: unknown): string {
5
22
  if (value instanceof HtmlSafeString) {
@@ -8,7 +25,7 @@ function escapeValue(value: unknown): string {
8
25
  } else if (Array.isArray(value)) {
9
26
  return value.map((val) => escapeValue(val)).join('');
10
27
  } else if (typeof value === 'string' || typeof value === 'number') {
11
- return ejs.escapeXML(String(value));
28
+ return escapeHtmlRaw(String(value));
12
29
  } else if (value == null) {
13
30
  // undefined or null -- render nothing
14
31
  return '';
@@ -37,7 +54,9 @@ export class HtmlSafeString {
37
54
  }
38
55
  }
39
56
 
40
- export function html(strings: TemplateStringsArray, ...values: any[]): HtmlSafeString {
57
+ export type HtmlValue = string | number | boolean | HtmlSafeString | undefined | null | HtmlValue[];
58
+
59
+ export function html(strings: TemplateStringsArray, ...values: HtmlValue[]): HtmlSafeString {
41
60
  return new HtmlSafeString(strings, values);
42
61
  }
43
62
 
@@ -47,7 +66,7 @@ export function html(strings: TemplateStringsArray, ...values: any[]): HtmlSafeS
47
66
  * popover.
48
67
  */
49
68
  export function escapeHtml(html: HtmlSafeString): HtmlSafeString {
50
- return unsafeHtml(ejs.escapeXML(html.toString()));
69
+ return unsafeHtml(escapeHtmlRaw(html.toString()));
51
70
  }
52
71
 
53
72
  /**
@@ -60,19 +79,3 @@ export function escapeHtml(html: HtmlSafeString): HtmlSafeString {
60
79
  export function unsafeHtml(value: string): HtmlSafeString {
61
80
  return new HtmlSafeString([value], []);
62
81
  }
63
-
64
- /**
65
- * This is a shim to allow for the use of EJS templates inside of HTML tagged
66
- * template literals.
67
- *
68
- * The resulting string is assumed to be appropriately escaped and will be used
69
- * verbatim in the resulting HTML.
70
- *
71
- * @param filename The name of the file from which relative includes should be resolved.
72
- * @param template The raw EJS template string.
73
- * @param data Any data to be made available to the template.
74
- * @returns The rendered EJS.
75
- */
76
- export function renderEjs(filename: string, template: string, data: any = {}): HtmlSafeString {
77
- return unsafeHtml(ejs.render(template, data, { views: [path.dirname(filename)] }));
78
- }
package/tsconfig.json CHANGED
@@ -1,16 +1,8 @@
1
1
  {
2
+ "extends": "@prairielearn/tsconfig",
2
3
  "compilerOptions": {
3
- "allowJs": true,
4
- "declaration": true,
5
- "esModuleInterop": true,
6
4
  "outDir": "./dist",
7
- "rootDir": "src/",
8
- "sourceMap": true,
9
- // This package will only be used server-side on Node 14+, so target the
10
- // newest version of the ES spec that Node 14 supports.
11
- "target": "ES2020",
12
- // However, we don't yet make extensive use of ES Modules, so specifically
13
- // compile `import`/`export` down to CommonJS.
14
- "module": "CommonJS",
15
- }
5
+ "rootDir": "./src",
6
+ "types": ["mocha"],
7
+ },
16
8
  }