๐ Online demo
Portable library that renders Hive posts and comments to string. It supports markdown and html and mimics the behaviour of condenser frontend.
Features:
- supports markdown and html
- sanitizes html and protects from XSS
- embeds images, videos, and other assets via links or iframes
- ensures links are safe to display and begins with
https://
protocol - linkify #tags and @username mentions
- proxify images if needed and appropriate function is provided
- synchronous execution with no external calls
Credit: this library is based on the code from condenser. It's aim is to allow other projects display Hive content the right way without porting the same code over and over.
Installation:
$ npm install --save @hiveio/content-renderer
Typescript:
import { DefaultRenderer } from "@hiveio/content-renderer";
const renderer = new DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url: string) => url,
usertagUrlFn: (account: string) => "/@" + account,
hashtagUrlFn: (hashtag: string) => "/trending/" + hashtag,
isLinkSafeFn: (url: string) => true,
addExternalCssClassToMatchingLinksFn: (url: string) => true,
ipfsPrefix: "https://ipfs.io/ipfs/" // IPFS gateway to display ipfs images
});
const safeHtmlStr = renderer.render(postContent);
See demo and its source.
<script src="https://unpkg.com/@hiveio/content-renderer"></script>
<script>
const renderer = new HiveContentRenderer.DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
doNotShowImages: false,
cssClassForInternalLinks: "link",
cssClassForExternalLinks: "external",
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => "/@" + account,
hashtagUrlFn: (hashtag) => "/trending/" + hashtag,
isLinkSafeFn: (url) => true,
addExternalCssClassToMatchingLinksFn: (url: string) => true,
ipfsPrefix: "https://ipfs.io/ipfs/"
});
$(document).ready(() => {
const renderMarkdownBtnElem = $("#render-button");
const inputElem = $("#input");
const outputElem = $("#output");
const outputMarkupElem = $("#output-markup");
renderMarkdownBtnElem.on("click", () => {
const input = inputElem.val();
const output = renderer.render(input);
console.log("Rendered", output);
outputElem.html(output);
outputMarkupElem.text(output);
});
});
</script>
</body>
</html>
You can pass options to the renderer to customize its behaviour. Here is the list of available
export interface RendererOptions {
baseUrl: string;
breaks: boolean;
skipSanitization: boolean;
allowInsecureScriptTags: boolean;
addNofollowToLinks: boolean;
doNotShowImages: boolean;
assetsWidth: number;
assetsHeight: number;
imageProxyFn: (url: string) => string;
hashtagUrlFn: (hashtag: string) => string;
usertagUrlFn: (account: string) => string;
isLinkSafeFn: (url: string) => boolean;
addExternalCssClassToMatchingLinksFn: (url: string) => boolean;
addTargetBlankToLinks?: boolean;
cssClassForInternalLinks?: string;
cssClassForExternalLinks?: string;
ipfsPrefix?: string;
}
baseUrl
- base url of the website. It's used to create links.breaks
- if true, newlines characters (\n
) are converted to<br>
tags. This only applies to markdown input. Usually you want to set this totrue
.skipSanitization
- if true, html is not sanitized. This is not recommended, as it can lead to XSS attacks. Set this tofalse
always for production use.allowInsecureScriptTags
- if true, script tags are not removed from the input. This is not recommended, as it can lead to XSS attacks. Set this tofalse
always for production use.addNofollowToLinks
- if true,rel="nofollow"
is added to all links.addTargetBlankToLinks
- if true,target="_blank"
is added to all links.doNotShowImages
- if true, images are not being rendered as<img>
tags but as<pre>
tags with the image url.assetsWidth
- width of the images and embeds in pixels.assetsHeight
- height of the images and embeds in pixels.imageProxyFn
- function that takes an image url and returns a proxied url. This can be useful to use a proxy to display images. It's also useful to resize images.hashtagUrlFn
- function that takes a hashtag and returns a url to the hashtag page.usertagUrlFn
- function that takes a usertag and returns a url to the user profile. This might be useful if you want to differentiate between internal and external links for some specific accounts, like bad actors.isLinkSafeFn
- function that takes a link and returns true if the link is safe to display. This can be useful to filter out links to phishing sites or other malicious content. If this function returns false, the link is not displayed and title is set to phishing warning.addExternalCssClassToMatchingLinksFn
- function that takes a link and returns true if the link should havecssClassForExternalLinks
added to it. This can be useful to differentiate appearance between internal and external links.addTargetBlankToLinks
- if true,target="_blank"
is added to all links.cssClassForInternalLinks
- if set, this class is added to all internal links.cssClassForExternalLinks
- if set, this class is added to all external links ifaddExternalCssClassToMatchingLinksFn
returns true.ipfsPrefix
- if set, this prefix is added to all IPFS links. This can be useful if you want to use a public IPFS gateway to display ipfs images. It may or may not end with a slash.
Library is written in typescript and expects NodeJS v20 or higher. If you use nvm, you can run nvm install
and nvm use
to switch to the right version automatically.
To start developing:
$ npm install
$ npm run build
The library is tested with mocha and chai. To run unit tests:
$ npm run test
Integration tests are run with testcafe. Please note you need to rebuild the library before running the tests in order to have the latest version of the library embedded in the test page. Run npm run build
before running the tests.
To run integration tests with your default browser (chrome):
$ npm run verify:chrome
$ npm run verify:firefox
In order to provide consistent code style, the library is linted with eslint with prettier and typescript plugin. This is enforced in CI and git hooks.
To run linter:
$ npm run lint
Library follows semantic versioning and is released to NPM registry automatically with CI after a merge to master. CI and husky is configured in a way to enforce Conventional Commits. Git hooks are installed to enforce this in local development.
Versioning is done automatically with semantic-release
. Please note that the version is bumped automatically based on the commit messages.