Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
A JSX based html templating engine for browsers or Node environments.
npm i jsxte
or
yarn add jsxte
To use the jsxte
you will have to set up your transpiler to use this package for transforming the JSX syntax, if you use typescript for transpiling all you have to do is set these options in the tsconfig:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "jsxte"
}
}
If you use something else, like babel you will also need to adapt the configuration of that, for example: https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#with-a-configuration-file-recommended
(See example configurations here).
Once you are done with that you can start writing your templates and rendering them.
import { createElement, renderToHtml } from "jsxte";
const Header: JSXTE.Component<{ label: string }> = (props) => {
return <h1>{props.label}</h1>;
};
const App: JSXTE.Component<{ label: string }> = (props) => {
return (
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="X-UA-Compatible"
content="IE=edge"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
</head>
<body>
<Header label={props.label} />
</body>
</html>
);
};
const html = renderToHtml(<App label="Hello World!" />);
// OR
const html = renderToHtml(createElement(App, { label: "Hello World!" }));
Check out these example repositories:
In case you use the templates in a server app in a Node environment you might want to include some data from the database in the html you serve to the client. To make it easier to fetch what's needed and marry it with the templates you can make your components asynchronous and send async requests from within them.
import { renderToHtmlAsync } from "jsxte";
const Header: JSXTE.Component = () => {
return <h1>Hello World</h1>;
};
const ToDoList: JSXTE.Component = async () => {
const todos = await fetchMyTodosFromDB();
return (
<table>
<thead>
<tr>
<th>Label</th>
<th>Is Done?</th>
</tr>
</thead>
<tbody>
{todos.map((todo) => (
<tr>
<td>{todo.label}</td>
<td>{todo.isDone ? "yes" : "no"}</td>
</tr>
))}
</tbody>
</table>
);
};
const App: JSXTE.Component = () => {
return (
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="X-UA-Compatible"
content="IE=edge"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
</head>
<body>
<Header />
<h3>ToDo's:</h3>
<ToDoList />
</body>
</html>
);
};
// If your component contains an asynchronous component at any point, `renderToHtmlAsync` needs to be used instead of `renderToHtml`
const html = await renderToHtmlAsync(<App label="Hello World!" />);
Context Map is a interface provided to each functional component that provides a mechanism for providing any arbitrary data to it's descendant. This is primarily to avoid the prop-drilling.
import { defineContext } from "jsxte";
const myContext = defineContext<{ label: string }>();
const App: JSXTE.Component = (props, componentApi) => {
// Set the context to a new value, all descendants of this component will have access to it
componentApi.ctx.set(myContext, { label: "Hello" });
return <Foo />;
};
const Foo: JSXTE.Component = (props, componentApi) => {
let label = "";
// Check if `myContext` is being provided by any of the ancestors
if (componentApi.ctx.has(myContext)) {
// Retrieve the context data
label = componentApi.ctx.getOrFail(myContext).label;
}
return <p>{label}</p>;
};
Context also provides a Provider and a Consumer components.
const MyContext = defineContext<string>();
const App: JSXTE.Component = () => {
return (
<MyContext.Provider value={"Hello World!"}>
<div>
<MyContext.Consumer
render={(providedValue) => <h1>{providedValue ?? ""}</h1>}
/>
</div>
</MyContext.Provider>
);
};
Error boundaries are components that catch errors thrown by their children and allow you to display a fallback UI instead of having the rendering outright fail.
Error boundaries work with both synchronous and asynchronous components. But the onError
handler should never return an asynchronous component.
import { ErrorBoundary, renderToHtml } from "jsxte";
class Boundary extends ErrorBoundary {
render(props: JSXTE.ElementProps, componentApi: ComponentApi) {
return <>{props.children}</>;
}
onError(
error: unknown,
originalProps: JSXTE.ElementProps,
componentApi: ComponentApi,
) {
return <h1>Something went wrong!</h1>;
}
}
const FailingComponent: JSXTE.Component = () => {
throw new Error("Unexpected failure!");
};
const html = renderToHtml(
<div>
<Boundary>
<FailingComponent />
</Boundary>
</div>,
);
// html:
// <div>
// <h1>Something went wrong!</h1>
// </div>
Symobl.toHtmlTag
is a special symbol that allows to determine how an object should be stringified when used as a child of a JSX element.
class User {
constructor(
public id: string,
public username: string,
public email: string,
) {}
[Symbol.toHtmlTag]() {
return `User: ${this.username}`;
}
}
const user = new User("001", "Johny", "johny0169@gmail.com");
renderToHtml(<div>{user}</div>);
Result:
<div>User: Johny</div>
DomRenderer
renders given JSX into a DOM object. It requires a window object to be passed to the constructor.
import { DomRenderer } from "jsxte";
const renderer = new DomRenderer(window);
const divElement = renderer.render(<div>Hello World!</div>);
divElement.outerHTML; // <div>Hello World!</div>
window.document.body.appendChild(divElement);
JsxteRenderer
is a base class around which HTML and JSON renderer are built upon. This renderer requires a specific interface that provides methods for creating the final output format:
// T is the type of the renderer return value
export interface ElementGenerator<T> {
createElement(
type: string,
attributes: Array<[attributeName: string, attributeValue: any]>,
children: Array<T>,
): T;
createTextNode(text: string | number | bigint): T;
createFragment(children: Array<T>): T;
}
It is possible to render to other formats than HTML or JSON by providing a custom ElementGenerator
implementation to the renderer.
import { JsxteRenderer } from "jsxte";
class DomGenerator
implements ElementGenerator<HTMLElement | Text | DocumentFragment>
{
createElement(
type: string,
attributes: Array<[attributeName: string, attributeValue: any]>,
children: Array<HTMLElement | Text | DocumentFragment>,
): HTMLElement | Text | DocumentFragment {
const element = document.createElement(type);
for (const [name, value] of attributes) {
element.setAttribute(name, value);
}
for (const child of children) {
element.appendChild(child);
}
return element;
}
createTextNode(
text: string | number | bigint,
): HTMLElement | Text | DocumentFragment {
return document.createTextNode(String(text));
}
createFragment(
children: Array<HTMLElement | Text | DocumentFragment>,
): HTMLElement | Text | DocumentFragment {
const fragment = document.createDocumentFragment();
for (const child of children) {
fragment.appendChild(child);
}
return fragment;
}
}
const renderer = new JsxteRenderer(new DomGenerator());
const divElement = renderer.render(<div>Hello World!</div>);
JSXTE should be able to parse any html attributes you put in, as well as custom web component tags, although you may see type errors if you use anything that is not defined in the library typings. If you wish to use them it is recommended you extend the typings to disable said errors.
To add a typing for a custom web component simply add a declare block in one of your project .ts
or .tsx
files, like this one:
declare global {
namespace JSX {
interface IntrinsicElements {
"my-custom-web-component": {
/* here include the attributes your component can take */
"data-example-attribute"?: string;
};
}
}
}
// with it it's possible to use this without type errors:
const MyComponent: JSXTE.Component = () => (
<my-custom-web-component data-example-attribute="Hello">
</my-custom-web-component>
);
There is a dictionary of html attributes that are available for every default html tag, that dictionary can be extended like so:
declare global {
namespace JSXTE {
interface BaseHTMLTagProps {
"new-attribute"?: string;
}
}
}
// with it it's possible to use this without type errors:
const MyComponent = () => <div new-attribute="Hello"></div>;
You can also use jsxte
with the Express View Engine. To do that, use the expressExtend
to add the engine support, specify the views directory and then use the express response method .render()
. The .render()
method takes the component props as it's second argument.
import express from "express";
import { expressExtend } from "jsxte";
const app = express();
expressExtend(app);
app.set("views", path.resolve(__dirname, "views"));
app.get("/", (_, resp) => {
const indexProps = {
/* ... */
};
resp.render("index", indexProps); // will render the `index.js` component located in the ./views file
});
For this approach to work, the JSX Components must be exported as defaults (ex. export default () => <div></div>
or exports.default = () => <div></div>
) and the views must be transpiled to .js
files.
It is possible to monkey-patch type definition of all HTML tags and add new attributes to them.
The following adds a new attribute to the <div />
tag - data-my-attr
:
declare global {
namespace JSXTE {
interface DivTagProps {
"data-my-attr"?: string;
}
}
}
The following adds a new attribute to all html tags - hx-post
:
declare global {
namespace JSXTE {
interface BaseHTMLTagProps {
"hx-post"?: string;
}
}
}
The following adds a Function
type to the onclick
attribute of all html tags:
declare global {
namespace JSXTE {
interface AttributeAcceptedTypes {
onclick?: Function;
}
}
}
If you want to contribute please See CONTRIBUTING.md
3.3.1 (March 16, 2024)
Added a new functionality that allows users to define a type that all attributes on all tags will accept.
class Box<T> {
constructor(public v: T) {}
toString() {
return String(this.v);
}
}
declare global {
namespace JSXTE {
interface AttributeAcceptedTypes {
ALL: Box<any>;
}
}
}
// thanks to the interface declared above, the following will not raise errors:
const div = <div class={new Box(123)}></div>; // ok
FAQs
JSX-based html templating engine for browsers or Node environments.
The npm package jsxte receives a total of 230 weekly downloads. As such, jsxte popularity was classified as not popular.
We found that jsxte demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.