@podium/podlet 5.2.4 → 5.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [5.3.0](https://github.com/podium-lib/podlet/compare/v5.2.4...v5.3.0) (2025-06-24)
2
+
3
+
4
+ ### Features
5
+
6
+ * add support for html tagged template literal ([#460](https://github.com/podium-lib/podlet/issues/460)) ([356244a](https://github.com/podium-lib/podlet/commit/356244a6d345f688037298830b05824487a12399))
7
+
1
8
  ## [5.2.4](https://github.com/podium-lib/podlet/compare/v5.2.3...v5.2.4) (2024-11-25)
2
9
 
3
10
 
package/README.md CHANGED
@@ -4,9 +4,7 @@ A Module for building page fragment servers in a micro frontend architecture.
4
4
 
5
5
  See the [official Podium documentation](https://podium-lib.io/) site.
6
6
 
7
- [![Dependencies](https://img.shields.io/david/podium-lib/podlet.svg?style=flat-square)](https://david-dm.org/podium-lib/podlet)
8
7
  [![GitHub Actions status](https://github.com/podium-lib/podlet/workflows/Run%20Lint%20and%20Tests/badge.svg)](https://github.com/podium-lib/podlet/actions?query=workflow%3A%22Run+Lint+and+Tests%22)
9
- [![Known Vulnerabilities](https://snyk.io/test/github/podium-lib/podlet/badge.svg?targetFile=package.json&style=flat-square)](https://snyk.io/test/github/podium-lib/podlet?targetFile=package.json)
10
8
 
11
9
  This is a module for building a podlet server. A podlet server is responsible for
12
10
  generating HTML fragments which can then be used in a [@podium/layout] server to compose a
@@ -22,8 +20,6 @@ convenience.
22
20
  For writing podlet servers with other HTTP frameworks, the following modules also exist:
23
21
 
24
22
  - [Fastify Podlet Plugin]
25
- - [Hapi Podlet Plugin]
26
- - [Koa Podlet Plugin]
27
23
 
28
24
  ## Installation
29
25
 
@@ -37,7 +33,7 @@ Building a simple podlet server using [Express].
37
33
 
38
34
  ```js
39
35
  import express from 'express';
40
- import Podlet from '@podium/podlet';
36
+ import Podlet, { html } from '@podium/podlet';
41
37
 
42
38
  // create a new podlet instance
43
39
  const podlet = new Podlet({
@@ -60,7 +56,7 @@ app.get(podlet.manifest(), (req, res) => {
60
56
 
61
57
  // create a route to serve the podlet's content
62
58
  app.get(podlet.content(), (req, res) => {
63
- res.podiumSend(`<div>hello world</div>`);
59
+ res.podiumSend(html`<div>hello world</div>`);
64
60
  });
65
61
 
66
62
  // start the app on port 7100
@@ -277,7 +273,7 @@ Please note the following caveats when using this feature:
277
273
  You can include a `<style>` tag before your content
278
274
 
279
275
  ```js
280
- res.podiumSend(`
276
+ res.podiumSend(html`
281
277
  <style>
282
278
  ...styles here...
283
279
  </style>
@@ -289,7 +285,7 @@ You can have your podlet CSS included for you by using the "shadow-dom" scope
289
285
 
290
286
  ```js
291
287
  podlet.css({ value: '/path/to/css', scope: 'shadow-dom' });
292
- res.podiumSend(`
288
+ res.podiumSend(html`
293
289
  <div>...content here...</div>
294
290
  `);
295
291
  ```
@@ -872,7 +868,7 @@ app.post(podlet.proxy({ target: '/api', name: 'api' }), (req, res) => { ... });
872
868
  // content route serving an HTML form
873
869
  app.get(podlet.content(), (req, res) => {
874
870
  const ctx = res.locals.podium.context;
875
- res.podiumSend(`
871
+ res.podiumSend(html`
876
872
  <form action="${ctx.mountOrigin}${ctx.publicPathname}/api" method="post">
877
873
  [ ... ]
878
874
  </form>
@@ -895,7 +891,7 @@ _Example of sending an HTML fragment:_
895
891
 
896
892
  ```js
897
893
  app.get(podlet.content(), (req, res) => {
898
- res.podiumSend('<h1>Hello World</h1>');
894
+ res.podiumSend(html`<h1>Hello World</h1>`);
899
895
  });
900
896
  ```
901
897
 
@@ -975,6 +971,49 @@ podlet.view(data => `<!doctype html>
975
971
  );
976
972
  ```
977
973
 
974
+ ## html
975
+
976
+ Tagged template literal that automatically escapes the different inputs to prevent XSS.
977
+
978
+ There are two exceptions that do not get escaped:
979
+
980
+ - [The result of a podlet `fetch`](https://github.com/podium-lib/client#fetchincoming-options) (relevant for layouts).
981
+ - [Strings wrapped in `DangerouslyIncludeUnescapedHTML`](#dangerouslyincludeunescapedhtml).
982
+
983
+ Use with [podiumSend](#respodiumsendfragment).
984
+
985
+ ```js
986
+ import { html } from "@podium/podlet";
987
+ ```
988
+
989
+ ## escape
990
+
991
+ The same escape function used by [`html`](#html) in case you want to escape something manually, for example in APIs.
992
+
993
+ ```js
994
+ import { escape } from "@podium/podlet";
995
+ ```
996
+
997
+ ## DangerouslyIncludeUnescapedHTML
998
+
999
+ Lets you opt a string you trust out of being escaped.
1000
+
1001
+ ```js
1002
+ import { html, DangerouslyIncludeUnescapedHTML } from "@podium/podlet";
1003
+
1004
+ const greeting = new DangerouslyIncludeUnescapedHTML({ __content: "<em>Howdy</em>" });
1005
+ const result = html`<p>${greeting} partner!</p>`
1006
+ ```
1007
+
1008
+ ## TemplateResult
1009
+
1010
+ This is the class type returned by the [`html`](#html) tagged template literal.
1011
+ You can use it for typing, or for advanced cases if `html` does not work for you.
1012
+
1013
+ ```js
1014
+ import { TemplateResult } from "@podium/podlet";
1015
+ ```
1016
+
978
1017
  ## Development mode
979
1018
 
980
1019
  In most cases podlets are fragments of a whole HTML document. When a layout
@@ -1046,10 +1085,10 @@ SOFTWARE.
1046
1085
  [@podium/layout]: https://github.com/podium-lib/layout '@podium/layout'
1047
1086
  [@podium/proxy]: https://github.com/podium-lib/proxy '@podium/proxy'
1048
1087
  [express]: https://expressjs.com/ 'Express'
1049
- [hapi podlet plugin]: https://github.com/podium-lib/hapi-podlet 'Hapi Podlet Plugin'
1050
1088
  [httpincoming]: https://github.com/podium-lib/utils/blob/main/lib/http-incoming.js 'HttpIncoming'
1051
1089
  [publicpathname]: https://github.com/podium-lib/context#public-pathname '`publicPathname`'
1052
1090
  [mountorigin]: https://github.com/podium-lib/context#mount-origin '`mountOrigin`'
1053
1091
  [abslog]: https://github.com/trygve-lie/abslog 'abslog'
1054
1092
  [pathname]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname 'pathname'
1055
1093
  [url]: https://developer.mozilla.org/en-US/docs/Web/API/URL 'URL'
1094
+ [Fastify Podlet Plugin]: https://github.com/podium-lib/fastify-podlet 'Fastify Podlet Plugin'
package/lib/podlet.js CHANGED
@@ -3,6 +3,10 @@ import {
3
3
  pathnameBuilder,
4
4
  AssetCss,
5
5
  AssetJs,
6
+ html,
7
+ escape,
8
+ DangerouslyIncludeUnescapedHTML,
9
+ TemplateResult,
6
10
  } from '@podium/utils';
7
11
  import * as schema from '@podium/schemas';
8
12
  import Metrics from '@metrics/client';
@@ -17,6 +21,8 @@ import { fileURLToPath } from 'node:url';
17
21
  import fs from 'node:fs';
18
22
  import assert from 'node:assert';
19
23
 
24
+ export { html, escape, DangerouslyIncludeUnescapedHTML, TemplateResult };
25
+
20
26
  const customElementRegex =
21
27
  /^(?!(annotation-xml|color-profile|font-face|font-face-src|font-face-uri|font-face-format|font-face-name|missing-glyph)$)[a-z][a-z0-9.-]*-[a-z0-9.-]*$/;
22
28
 
@@ -831,16 +837,34 @@ export default class PodiumPodlet {
831
837
  *
832
838
  * @template {{ [key: string]: unknown }} T
833
839
  * @param {HttpIncoming<T>} incoming - Instance of Podium HttpIncoming object
834
- * @param {string} data - the podlet content as an HTML markup string
840
+ * @param {string | TemplateResult} data - the podlet content as an HTML markup string
835
841
  * @param {...any} args - additional args depending on the template and what values it accepts
836
842
  * @returns {string}
837
843
  */
838
844
  render(incoming, data, ...args) {
839
- const markup = this.#useShadowDOM ? this.wrapWithShadowDOM(data) : data;
845
+ let dataString;
846
+ const tag = Object.prototype.toString.call(data);
847
+ if (tag === '[object TemplateResult]') {
848
+ dataString = /** @type {TemplateResult} */ (data).content;
849
+ } else {
850
+ dataString = /** @type {string} */ (data);
851
+ }
852
+
853
+ const markup = this.#useShadowDOM
854
+ ? this.wrapWithShadowDOM(dataString)
855
+ : dataString;
840
856
  if (!incoming.development) {
841
857
  return markup;
842
858
  }
843
- return this.#view(incoming, markup, ...args);
859
+
860
+ const newArgs = args.map((arg) => {
861
+ const tag = Object.prototype.toString.call(arg);
862
+ if (tag === '[object TemplateResult]') {
863
+ return /** @type {TemplateResult} */ (arg).content;
864
+ }
865
+ return arg;
866
+ });
867
+ return this.#view(incoming, markup, ...newArgs);
844
868
  }
845
869
 
846
870
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podium/podlet",
3
- "version": "5.2.4",
3
+ "version": "5.3.0",
4
4
  "type": "module",
5
5
  "description": "Module for building page fragment servers in a micro frontend architecture.",
6
6
  "license": "MIT",
@@ -35,24 +35,24 @@
35
35
  "dependencies": {
36
36
  "@metrics/client": "2.5.4",
37
37
  "@podium/schemas": "5.1.0",
38
- "@podium/proxy": "5.0.32",
39
- "@podium/utils": "5.4.0",
38
+ "@podium/proxy": "5.0.33",
39
+ "@podium/utils": "5.5.0",
40
40
  "abslog": "2.4.4",
41
41
  "ajv": "8.17.1",
42
42
  "objobj": "1.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@podium/eslint-config": "1.0.2",
45
+ "@podium/eslint-config": "1.0.11",
46
46
  "@podium/semantic-release-config": "2.0.0",
47
- "@podium/test-utils": "3.0.11",
47
+ "@podium/test-utils": "3.0.17",
48
48
  "@podium/typescript-config": "1.0.0",
49
- "@types/node": "20.16.10",
50
- "@types/readable-stream": "4.0.18",
51
- "eslint": "9.13.0",
49
+ "@types/node": "20.17.57",
50
+ "@types/readable-stream": "4.0.20",
51
+ "eslint": "9.25.1",
52
52
  "express": "4.20.0",
53
53
  "json-stringify-safe": "5.0.1",
54
54
  "npm-run-all2": "6.2.6",
55
- "prettier": "3.3.3",
55
+ "prettier": "3.5.3",
56
56
  "semantic-release": "24.1.2",
57
57
  "tap": "18.8.0",
58
58
  "typescript": "5.5.4",
package/types/podlet.d.ts CHANGED
@@ -586,13 +586,13 @@ export default class PodiumPodlet {
586
586
  *
587
587
  * @template {{ [key: string]: unknown }} T
588
588
  * @param {HttpIncoming<T>} incoming - Instance of Podium HttpIncoming object
589
- * @param {string} data - the podlet content as an HTML markup string
589
+ * @param {string | TemplateResult} data - the podlet content as an HTML markup string
590
590
  * @param {...any} args - additional args depending on the template and what values it accepts
591
591
  * @returns {string}
592
592
  */
593
593
  render<T extends {
594
594
  [key: string]: unknown;
595
- }>(incoming: HttpIncoming<T>, data: string, ...args: any[]): string;
595
+ }>(incoming: HttpIncoming<T>, data: string | TemplateResult, ...args: any[]): string;
596
596
  /**
597
597
  * Function that takes in the podlet content,wraps it in declarative shadow DOM and returns it.
598
598
  * @param {string} data - the podlet content to wrap in declarative shadow DOM as an HTML markup string
@@ -724,8 +724,13 @@ export type AssetJsLike = {
724
724
  scope?: "content" | "fallback" | "all" | "shadow-dom";
725
725
  [key: string]: any;
726
726
  };
727
+ import { html } from '@podium/utils';
728
+ import { escape } from '@podium/utils';
729
+ import { DangerouslyIncludeUnescapedHTML } from '@podium/utils';
730
+ import { TemplateResult } from '@podium/utils';
727
731
  import Proxy from '@podium/proxy';
728
732
  import { AssetCss } from '@podium/utils';
729
733
  import { AssetJs } from '@podium/utils';
730
734
  import Metrics from '@metrics/client';
731
735
  import { HttpIncoming } from '@podium/utils';
736
+ export { html, escape, DangerouslyIncludeUnescapedHTML, TemplateResult };