Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@kitajs/html

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@kitajs/html - npm Package Compare versions

Comparing version 2.2.2 to 3.0.0

.prettierrc.js

4

all-types.d.ts

@@ -7,8 +7,8 @@ // This file extends JSX namespace to allow any tag/attribute combination.

interface HtmlTag {
[name: string]: unknown
[name: string]: unknown;
}
interface IntrinsicElements {
[name: string]: HtmlTag
[name: string]: HtmlTag;
}
}

@@ -10,7 +10,5 @@ // This file is a result from https://github.com/Desdaemon/typed-htmx and https://htmx.org

declare namespace Htmx {
/**
* Either `true`, `false`, `"true"` or `"false"`.
*/
type BoolStr = boolean | 'true' | 'false'
type AnyStr = string & {}
/** Either `true`, `false`, `"true"` or `"false"`. */
type BoolStr = boolean | 'true' | 'false';
type AnyStr = string & {};
type HxSwap =

@@ -26,12 +24,11 @@ | 'innerHTML'

| 'morph'
| 'morphdom'
| 'morphdom';
/**
* Either `this` which refers to the element itself, or a modifier followed by a CSS selector, e.g. `closest form`.
* Either `this` which refers to the element itself, or a modifier followed by a CSS
* selector, e.g. `closest form`.
*/
type HxTarget = 'this' | 'closest ' | 'find ' | 'next ' | 'previous '
type HxTarget = 'this' | 'closest ' | 'find ' | 'next ' | 'previous ';
/**
* A CSS selector, followed by one of these sync strategies, e.g. `form:abort`.
*/
/** A CSS selector, followed by one of these sync strategies, e.g. `form:abort`. */
type HxSync =

@@ -44,12 +41,8 @@ | ':drop'

| ':queue last'
| ':queue all'
| ':queue all';
/**
* Evaluate the values given, you can prefix the values with javascript: or js:.
*/
type HxHeaders = AnyStr | 'javascript:' | 'js:'
/** Evaluate the values given, you can prefix the values with javascript: or js:. */
type HxHeaders = AnyStr | 'javascript:' | 'js:';
/**
* An event followed by one of these modifiers, e.g. `click once`.
*/
/** An event followed by one of these modifiers, e.g. `click once`. */
type HxTriggerModifier =

@@ -66,3 +59,3 @@ | ' once'

| ' queue:all'
| ' queue:none'
| ' queue:none';

@@ -94,8 +87,10 @@ /**

/**
* Includes the commonly-used `X-Requested-With` header that identifies ajax requests in many backend frameworks.
* Includes the commonly-used `X-Requested-With` header that identifies ajax requests
* in many backend frameworks.
*
* CDN: https://unpkg.com/htmx.org/dist/ext/ajax-header.js
*
* @see https://htmx.org/extensions/ajax-header/
*/
ajaxHeaders: 'ajax-headers'
ajaxHeaders: 'ajax-headers';

@@ -106,5 +101,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/sse.js
*
* @see https://htmx.org/extensions/server-sent-events/
*/
serverSentEvents: 'sse'
serverSentEvents: 'sse';

@@ -115,5 +111,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/ws.js
*
* @see https://htmx.org/extensions/web-sockets/
*/
ws: 'ws'
ws: 'ws';

@@ -124,5 +121,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/class-tools.js
*
* @see https://htmx.org/extensions/class-tools/
*/
classTools: 'class-tools'
classTools: 'class-tools';

@@ -133,5 +131,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/debug.js
*
* @see https://htmx.org/extensions/debug/
*/
debug: 'debug'
debug: 'debug';

@@ -142,5 +141,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/disable-element.js
*
* @see https://htmx.org/extensions/disable-element/
*/
disableElement: 'disable-element'
disableElement: 'disable-element';

@@ -151,5 +151,6 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/event-header.js
*
* @see https://htmx.org/extensions/event-header/
*/
eventHeader: 'event-header'
eventHeader: 'event-header';

@@ -160,21 +161,26 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/head-support.js
*
* @see https://htmx.org/extensions/head-support/
*/
headSupport: 'head-support'
headSupport: 'head-support';
/**
* Support for [Idiomorph](https://github.com/bigskysoftware/idiomorph), an alternative swapping mechanism for htmx.
* Support for [Idiomorph](https://github.com/bigskysoftware/idiomorph), an
* alternative swapping mechanism for htmx.
*
* CDN: https://unpkg.com/idiomorph/dist/idiomorph-ext.min.js
*
* @see https://github.com/bigskysoftware/idiomorph#htmx
*/
idiomorph: 'morph'
idiomorph: 'morph';
/**
* Use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded`.
* Use JSON encoding in the body of requests, rather than the default
* `x-www-form-urlencoded`.
*
* CDN: https://unpkg.com/htmx.org/dist/ext/json-enc.js
*
* @see https://htmx.org/extensions/json-enc/
*/
jsonEncode: 'json-enc'
jsonEncode: 'json-enc';

@@ -185,52 +191,58 @@ /**

* CDN: https://unpkg.com/htmx.org/dist/ext/loading-states.js
*
* @see https://htmx.org/extensions/loading-states/
*/
loadingStates: 'loading-states'
loadingStates: 'loading-states';
/**
* Support for [morphdom](https://github.com/patrick-steele-idem/morphdom),
* an alternative swapping mechanism for htmx.
* Support for [morphdom](https://github.com/patrick-steele-idem/morphdom), an
* alternative swapping mechanism for htmx.
*
* CDN: https://unpkg.com/htmx.org/dist/ext/morphdom-swap.js
*
* @see https://htmx.org/extensions/morphdom-swap/
*/
morphdom: 'morphdom'
morphdom: 'morphdom';
}
/**
* Definitions for htmx attributes up to 1.9.3.
*/
/** Definitions for htmx attributes up to 1.9.3. */
interface Attributes {
/**
* Issues a `GET` to the specified URL.
*
* @see https://htmx.org/attributes/hx-get/
*/
['hx-get']?: string
['hx-get']?: string;
/**
* Issues a `POST` to the specified URL.
*
* @see https://htmx.org/attributes/hx-post/
*/
['hx-post']?: string
['hx-post']?: string;
/**
* Issues a `PUT` to the specified URL.
*
* @see https://htmx.org/attributes/hx-put/
*/
['hx-put']?: string
['hx-put']?: string;
/**
* Issues a `DELETE` to the specified URL.
*
* @see https://htmx.org/attributes/hx-delete/
*/
['hx-delete']?: string
['hx-delete']?: string;
/**
* Issues a `PATCH` to the specified URL.
*
* @see https://htmx.org/attributes/hx-patch/
*/
['hx-patch']?: string
['hx-patch']?: string;
/**
* Add or remove [progressive enhancement] for links and forms.
*
* @see https://htmx.org/attributes/hx-boost/

@@ -240,234 +252,273 @@ *

*/
['hx-boost']?: BoolStr
['hx-boost']?: BoolStr;
/**
* Handle any event with a script inline.
* @see https://htmx.org/attributes/hx-on/
* @remarks Event listeners on htmx-specific events need to be specified with a spread attribute, and
* are otherwise not supported in vanilla JSX.
*
* @remarks
* Event listeners on htmx-specific events need to be specified with a spread
* attribute, and are otherwise not supported in vanilla JSX.
*
* ```jsx
* <div {...{'hx-on::before-request': '...'}} />
* <div {...{ 'hx-on::before-request': '...' }} />;
* ```
* @since 1.9.3
* @see https://htmx.org/attributes/hx-on/
*/
[`hx-on:`]?: string
[`hx-on:`]?: string;
/**
* Handle any event with a script inline. Each listener is specified on a separate line.
* Handle any event with a script inline. Each listener is specified on a separate
* line.
*
* @remarks
* Superseded by `hx-on:$event`, unless IE11 support is required.
* @since 1.9.0
* @see https://htmx.org/attributes/hx-on/
* @remarks Superseded by `hx-on:$event`, unless IE11 support is required.
* @since 1.9.0
*/
['hx-on']?: string
['hx-on']?: string;
/**
* Pushes the URL into the browser location bar, creating a new history entry.
*
* @see https://htmx.org/attributes/hx-push-url/
*/
['hx-push-url']?: BoolStr | AnyStr
['hx-push-url']?: BoolStr | AnyStr;
/**
* Select content to swap in from a response.
*
* @see https://htmx.org/attributes/hx-select/
*/
['hx-select']?: string
['hx-select']?: string;
/**
* Select content to swap in from a response, out of band (somewhere other than the target).
* Select content to swap in from a response, out of band (somewhere other than the
* target).
*
* @see https://htmx.org/attributes/hx-select-oob/
*/
['hx-select-oob']?: string
['hx-select-oob']?: string;
/**
* Controls how content is swapped in (`outerHTML`, `beforeend`, `afterend`, …).
* @see https://htmx.org/attributes/hx-swap/
* @see {@linkcode InsertPosition} which is used in [{@linkcode Element.insertAdjacentHTML}](https://developer.mozilla.org/docs/Web/API/Element/insertAdjacentHTML)
*
* @remarks
* - `morph` swaps are part of the {@linkcode Extensions.idiomorph idiomorph} extension.
* - `morphdom` swaps are part of the {@linkcode Extensions.morphdom morphdom} extension.
*
* @see https://htmx.org/attributes/hx-swap/
* @see {@linkcode InsertPosition} which is used in [{@linkcode Element.insertAdjacentHTML}](https://developer.mozilla.org/docs/Web/API/Element/insertAdjacentHTML)
*/
['hx-swap']?: HxSwap | AnyStr
['hx-swap']?: HxSwap | AnyStr;
/**
* Marks content in a response to be out of band (should swap in somewhere other than the target).
* Marks content in a response to be out of band (should swap in somewhere other than
* the target).
*
* @see https://htmx.org/attributes/hx-swap-oob/
*/
['hx-swap-oob']?: 'true' | HxSwap | AnyStr
['hx-swap-oob']?: 'true' | HxSwap | AnyStr;
/**
* Specifies the target element to be swapped.
*
* @see https://htmx.org/attributes/hx-target/
*/
['hx-target']?: HxTarget | AnyStr
['hx-target']?: HxTarget | AnyStr;
/**
* Specifies the event that triggers the request.
*
* @see https://htmx.org/attributes/hx-trigger/
*/
['hx-trigger']?: 'every ' | HxTriggerModifier | AnyStr
['hx-trigger']?: 'every ' | HxTriggerModifier | AnyStr;
/**
* Adds values to the parameters to submit with the request (JSON-formatted).
*
* @see https://htmx.org/attributes/hx-vals/
*/
['hx-vals']?: HxHeaders
['hx-vals']?: HxHeaders;
/**
* Shows a `confirm()` dialog before issuing a request.
*
* @see https://htmx.org/attributes/hx-confirm/
*/
['hx-confirm']?: string
['hx-confirm']?: string;
/**
* Disables htmx processing for the given node and any children nodes.
*
* @see https://htmx.org/attributes/hx-disable/
*/
['hx-disable']?: boolean
['hx-disable']?: boolean;
/**
* Control and disable automatic attribute inheritance for child nodes.
*
* @see https://htmx.org/attributes/hx-disinherit/
*/
['hx-disinherit']?: '*' | AnyStr
['hx-disinherit']?: '*' | AnyStr;
/**
* Changes the request encoding type.
*
* @see https://htmx.org/attributes/hx-encoding/
*/
['hx-encoding']?: 'multipart/form-data'
['hx-encoding']?: 'multipart/form-data';
/**
* Extensions to use for this element.
*
* @see https://htmx.org/attributes/hx-ext/
* @see {@linkcode Extensions} for how to declare extensions in JSX.
*/
['hx-ext']?: Htmx.Extensions[keyof Htmx.Extensions] | 'ignore:' | AnyStr
['hx-ext']?: Htmx.Extensions[keyof Htmx.Extensions] | 'ignore:' | AnyStr;
/**
* Adds to the headers that will be submitted with the request.
*
* @see https://htmx.org/attributes/hx-headers/
*/
['hx-headers']?: HxHeaders | AnyStr
['hx-headers']?: HxHeaders | AnyStr;
/**
* Prevent sensitive data being saved to the history cache.
*
* @see https://htmx.org/attributes/hx-history/
*/
['hx-history']?: 'false'
['hx-history']?: 'false';
/**
* The element to snapshot and restore during history navigation.
*
* @see https://htmx.org/attributes/hx-history-elt/
*/
['hx-history-elt']?: boolean
['hx-history-elt']?: boolean;
/**
* Include additional data in requests.
*
* @see https://htmx.org/attributes/hx-include/
*/
['hx-include']?: string
['hx-include']?: string;
/**
* The element to put the `htmx-request` class on during the request.
*
* @see https://htmx.org/attributes/hx-indicator/
*/
['hx-indicator']?: string
['hx-indicator']?: string;
/**
* Filters the parameters that will be submitted with a request.
*
* @see https://htmx.org/attributes/hx-params/
*/
['hx-params']?: '*' | 'none' | 'not ' | AnyStr
['hx-params']?: '*' | 'none' | 'not ' | AnyStr;
/**
* Specifies elements to keep unchanged between requests.
*
* @remarks
* `true` is only observed by the `head-support` extension, where it prevents an
* element from being removed from the `<head>`.
* @see https://htmx.org/attributes/hx-preserve/
* @remarks `true` is only observed by the `head-support` extension,
* where it prevents an element from being removed from the `<head>`.
*/
['hx-preserve']?: boolean | 'true'
['hx-preserve']?: boolean | 'true';
/**
* Shows a `prompt()` before submitting a request.
*
* @see https://htmx.org/attributes/hx-prompt/
*/
['hx-prompt']?: string
['hx-prompt']?: string;
/**
* Replace the URL in the browser location bar.
*
* @see https://htmx.org/attributes/hx-replace-url/
*/
['hx-replace-url']?: BoolStr | AnyStr
['hx-replace-url']?: BoolStr | AnyStr;
/**
* Configures various aspects of the request.
*
* @see https://htmx.org/attributes/hx-request/
*/
['hx-request']?:
| `"timeout": `
| `"credentials": `
| `"noHeaders": `
| HxHeaders
['hx-request']?: `"timeout": ` | `"credentials": ` | `"noHeaders": ` | HxHeaders;
/**
* Control how requests made by different elements are synchronized.
*
* @see https://htmx.org/attributes/hx-sync/
*/
['hx-sync']?: HxSync
['hx-sync']?: HxSync;
/**
* Force elements to validate themselves before a request.
*
* @see https://htmx.org/attributes/hx-validate/
*/
['hx-validate']?: boolean
['hx-validate']?: boolean;
/**
* Adds values dynamically to the parameters to submit with the request.
* @deprecated superseded by `hx-vals`
*
* @deprecated Superseded by `hx-vals`
*/
['hx-vars']?: AnyStr
['hx-vars']?: AnyStr;
/**
* The URL of the SSE server.
*
* @see https://htmx.org/extensions/server-sent-events/
*/
['sse-connect']?: string
['sse-connect']?: string;
/**
* The name of the message to swap into the DOM.
*
* @see https://htmx.org/extensions/server-sent-events/
*/
['sse-swap']?: string
['sse-swap']?: string;
/**
* A URL to establish a WebSocket connection against.
*
* @see https://htmx.org/extensions/web-sockets/
*/
['ws-connect']?: string
['ws-connect']?: string;
/**
* Sends a message to the nearest websocket based on the trigger value for the element.
* Sends a message to the nearest websocket based on the trigger value for the
* element.
*
* @see https://htmx.org/extensions/web-sockets/
*/
['ws-send']?: boolean
['ws-send']?: boolean;
/**
* Apply class transitions on this element.
*
* @see https://htmx.org/extensions/class-tools/
*/
['classes']?: `add ` | `remove ` | `toggle ` | AnyStr
['classes']?: `add ` | `remove ` | `toggle ` | AnyStr;
/**
* The element or elements to disable during requests.
* Accepts CSS selectors.
* The element or elements to disable during requests. Accepts CSS selectors.
*
* @see https://htmx.org/extensions/disable-element/
*/
['hx-disable-element']?: 'self' | AnyStr
['hx-disable-element']?: 'self' | AnyStr;
/**
* The strategy for merging new head content.
*
* @see https://htmx.org/extensions/head-support/
*/
['hx-head']?: 'merge' | 'append' | 're-eval'
['hx-head']?: 'merge' | 'append' | 're-eval';
}
}
/// <reference path="./jsx.d.ts" />
type Html = typeof Html;
/**

@@ -8,3 +10,2 @@ * Fast and type safe HTML templates using JSX syntax.

* @license Apache License Version 2.0
*
* @link https://github.com/kitajs/html

@@ -16,26 +17,23 @@ * @link https://www.npmjs.com/package/@kitajs/html

/**
* A const used to represent a html fragment.
*
*/
export const Fragment: unique symbol
/**
* Returns true if the character at the given index is an uppercase character.
*
* @param {string} input the string to check.
* @param {number} index the index of the character to check.
* @returns {boolean} if the character at the given index is an uppercase character.
* @param {string} input The string to check.
* @param {number} index The index of the character to check.
* @returns {boolean} If the character at the given index is an uppercase character.
* @this {void}
*/
export function isUpper(this: void, input: string, index: number): boolean
export function isUpper(this: void, input: string, index: number): boolean;
/**
* Escapes a string for safe use as HTML text content. If the value is not a string,
* it is coerced to one with its own `toString()` method.
* Escapes a string for safe use as HTML text content. If the value is not a string, it
* is coerced to one with its own `toString()` method.
*
* @param {unknown} value the value to escape.
* @returns {string} the escaped string.
* If the {@linkcode Bun} runtime is available, this function will be swapped out to
* {@linkcode Bun.escapeHTML}.
*
* @param {unknown} value The value to escape.
* @returns {string} The escaped string.
* @this {void}
*/
export function escapeHtml(this: void, value: any): string
export function escapeHtml(this: void, value: any): string;

@@ -45,7 +43,7 @@ /**

*
* @param {string} tag the name of the element to check.
* @returns {boolean} if the element is a html void element.
* @param {string} tag The name of the element to check.
* @returns {boolean} If the element is a html void element.
* @this {void}
*/
export function isVoidElement(this: void, tag: string): boolean
export function isVoidElement(this: void, tag: string): boolean;

@@ -55,7 +53,8 @@ /**

*
* @param {object | string} style a record of literal values to use as style attributes or a string.
* @returns {string} the generated html style string.
* @param {object | string} style A record of literal values to use as style attributes
* or a string.
* @returns {string} The generated html style string.
* @this {void}
*/
export function styleToString(this: void, style: object | string): string
export function styleToString(this: void, style: object | string): string;

@@ -67,9 +66,9 @@ /**

*
* @example `a b="c" d="1"`
* @example ;`a b="c" d="1"`
*
* @param {object} attributes a record of literal values to use as attributes.
* @returns {string} the generated html attributes string.
* @param {object} attributes A record of literal values to use as attributes.
* @returns {string} The generated html attributes string.
* @this {void}
*/
export function attributesToString(this: void, attributes: object): string
export function attributesToString(this: void, attributes: object): string;

@@ -79,6 +78,6 @@ /**

*
* @param {string} camel the camel cased string to convert.
* @param {string} camel The camel cased string to convert.
* @this {void}
*/
export function toKebabCase(this: void, camel: string): string
export function toKebabCase(this: void, camel: string): string;

@@ -88,14 +87,21 @@ /**

*
* @param {string | Function} name the name of the element to create or a function that creates the element.
* @param {{children?: object}} [attributes] a record of literal values to use as attributes. A property named `children` will be used as the children of the element.
* @param {...string} contents the inner contents of the element.
* @returns {string} the generated html string.
* @param {string | Function} name The name of the element to create or a function that
* creates the element.
* @param {{ children?: object }} [attributes] A record of literal values to use as
* attributes. A property named `children` will be used as the children of the
* element.
* @param {...string} contents The inner contents of the element.
* @returns {string} The generated html string.
* @this {void}
*/
export function createElement(
export function createElement<C extends Children[], N extends string | Function>(
this: void,
name: string | Function | typeof Fragment,
name: N,
attributes: PropsWithChildren<any> | null,
...contents: Children[]
): JSX.Element
...contents: C
): Promise<string> extends C[number]
? Promise<string>
: N extends () => Promise<string>
? Promise<string>
: string;

@@ -107,46 +113,39 @@ /**

*
* @param {import('.').Children[]} contents an maybe nested array of strings to concatenate.
* @param {boolean} [escape=false] if we should escape the contents before concatenating them.
* @returns {string} the concatenated and escaped string of contents.
* @param {import('.').Children[]} contents An maybe nested array of strings to
* concatenate.
* @param {boolean} [escape=false] If we should escape the contents before concatenating
* them. Default is `false`
* @returns {string} The concatenated and escaped string of contents.
* @this {void}
*/
export function contentsToString(
export function contentsToString<C extends Children[]>(
this: void,
contents: Children[],
contents: C,
escape?: boolean
): JSX.Element
): Promise<string> extends C[number] ? Promise<string> : string;
/**
* Compiles a **clean component** into a super fast component. This does not
* support unclean components / props processing.
* Compiles a **clean component** into a super fast component. This does not support
* unclean components / props processing.
*
* A **clean component** is a component that does not process props before
* applying them to the element. This means that the props are applied to the
* element as is, and you need to process them before passing them to the
* component.
* A **clean component** is a component that does not process props before applying them
* to the element. This means that the props are applied to the element as is, and you
* need to process them before passing them to the component.
*
* @example
* ```tsx
* // Clean component, render as is
* function Clean(props: PropsWithChildren<{ repeated: string }>) {
* return <div>{props.repeated}</div>
* }
* @example ;```tsx // Clean component, render as is function Clean(props:
* PropsWithChildren<{ repeated: string }>) { return <div>{props.repeated}</div> }
*
* // Calculation is done before passing to the component
* html = <Clean name={'a'.repeat(5)} />
* // Calculation is done before passing to the component html = <Clean
* name={'a'.repeat(5)} />
*
* // Unclean component, process before render
* function Unclean(props: { repeat: string; n: number }) {
* return <div>{props.repeat.repeat(props.n)}</div>
* }
* // Unclean component, process before render function Unclean(props: { repeat: string;
* n: number }) { return <div>{props.repeat.repeat(props.n)}</div> }
*
* // Calculation is done inside the component, thus cannot be used with .compile()
* html = <Unclean repeat="a" n={5} />
* ```
* // Calculation is done inside the component, thus cannot be used with .compile() html
* = <Unclean repeat="a" n={5} />
*
* @param {Function} htmlComponent the *clean* component to compile.
* @param {boolean} [strict=true] if we should throw an error when a property is not found.
* @param {string | undefined} [separator] the string used to interpolate and separate parameters
* @returns {Function} the compiled template function
* @this {void}
* @param {Function} htmlComponent The _clean_ component to compile. @param {boolean}
* [strict=true] If we should throw an error when a property is not found. Default is
* `true` @param {string | undefined} [separator] The string used to interpolate and
* separate parameters @returns {Function} The compiled template function @this {void}
*/

@@ -160,8 +159,17 @@ export function compile<

separator?: string
): Component<P>
): Component<P>;
/** Here for interop with `preact` and many build systems. */
export const h: typeof createElement;
/**
* Here for interop with `preact` and many build systems.
* A JSX Fragment is used to return multiple elements from a component.
*
* @example ;```tsx // renders <div>1</div> and <div>2</div> without needing a wrapper
* element const html = <><div>1</div><div>2</div></>
*
* // Html.Fragment is the same as <>...</> const html =
* <Html.Fragment><div>1</div><div>2</div></Html.Fragment>
*/
export const h: typeof createElement
export function Fragment(props: PropsWithChildren): JSX.Element;

@@ -174,5 +182,6 @@ export type Children =

| undefined
| Children[]
| Promise<Children>
| Children[];
export type PropsWithChildren<T = {}> = { children?: Children } & T
export type PropsWithChildren<T = {}> = { children?: Children } & T;

@@ -182,5 +191,16 @@ export type Component<T = {}> = (

props: PropsWithChildren<T>
) => JSX.Element
) => JSX.Element;
/**
* Fast and type safe HTML templates using JSX syntax.
*
* @module html
* @license Apache License Version 2.0
* @link https://github.com/kitajs/html
* @link https://www.npmjs.com/package/@kitajs/html
* @link https://kitajs.github.io/html/
*/
export const Html: Html;
}
export = Html
export = Html;
/// <reference path="./jsx.d.ts" />
const ESCAPED_REGEX = /[<"'&]/
const CAMEL_REGEX = /[a-z][A-Z]/
const ESCAPED_REGEX = /[<"'&]/;
const CAMEL_REGEX = /[a-z][A-Z]/;
/**
* @type {typeof import('.').Fragment}
*/
const Fragment = Symbol.for('kHtmlFragment')
/**
* @type {import('.').isUpper}
*/
function isUpper (input, index) {
const code = input.charCodeAt(index)
return code >= 65 /* A */ && code <= 90 /* Z */
/** @type {import('.').isUpper} */
function isUpper(input, index) {
const code = input.charCodeAt(index);
return code >= 65 /* A */ && code <= 90; /* Z */
}
/**
* @type {import('.').toKebabCase}
*/
function toKebabCase (camel) {
/** @type {import('.').toKebabCase} */
function toKebabCase(camel) {
// This is a optimization to avoid the whole conversion process when the
// string does not contain any uppercase characters.
if (!CAMEL_REGEX.test(camel)) {
return camel
return camel;
}
const length = camel.length
const length = camel.length;
let start = 0
let end = 0
let kebab = ''
let prev = true
let curr = isUpper(camel, 0)
let next
let start = 0;
let end = 0;
let kebab = '';
let prev = true;
let curr = isUpper(camel, 0);
let next;
for (; end < length; end++) {
next = isUpper(camel, end + 1)
next = isUpper(camel, end + 1);

@@ -44,22 +35,20 @@ // detects the start of a new camel case word and avoid lowercasing abbreviations.

// @ts-expect-error - this indexing is safe.
kebab += camel.slice(start, end) + '-' + camel[end].toLowerCase()
start = end + 1
kebab += camel.slice(start, end) + '-' + camel[end].toLowerCase();
start = end + 1;
}
prev = curr
curr = next
prev = curr;
curr = next;
}
// Appends the remaining string.
kebab += camel.slice(start, end)
kebab += camel.slice(start, end);
return kebab
return kebab;
}
/**
* @type {import('.').escapeHtml}
*/
function escapeHtml (value) {
/** @type {import('.').escapeHtml} */
let escapeHtml = function (value) {
if (typeof value !== 'string') {
value = value.toString()
value = value.toString();
}

@@ -70,11 +59,12 @@

if (!ESCAPED_REGEX.test(value)) {
return value
return value;
}
const length = value.length
let escaped = ''
const length = value.length;
let escaped = '';
let start = 0
let end = 0
let start = 0;
let end = 0;
// Escapes double quotes to be used inside attributes
// Faster than using regex

@@ -86,19 +76,19 @@ // https://jsperf.app/kakihu

case '&':
escaped += value.slice(start, end) + '&amp;'
start = end + 1
continue
escaped += value.slice(start, end) + '&amp;';
start = end + 1;
continue;
// We don't need to escape > because it is only used to close tags.
// https://stackoverflow.com/a/9189067
case '<':
escaped += value.slice(start, end) + '&lt;'
start = end + 1
continue
escaped += value.slice(start, end) + '&lt;';
start = end + 1;
continue;
case '"':
escaped += value.slice(start, end) + '&#34;'
start = end + 1
continue
escaped += value.slice(start, end) + '&#34;';
start = end + 1;
continue;
case "'":
escaped += value.slice(start, end) + '&#39;'
start = end + 1
continue
escaped += value.slice(start, end) + '&#39;';
start = end + 1;
continue;
}

@@ -108,11 +98,13 @@ }

// Appends the remaining string.
escaped += value.slice(start, end)
escaped += value.slice(start, end);
return escaped
}
return escaped;
};
/**
* @type {import('.').isVoidElement}
*/
function isVoidElement (tag) {
/* c8 ignore next 2 */
// @ts-ignore - bun runtime have its own escapeHTML function.
if (typeof Bun !== 'undefined') escapeHtml = Bun.escapeHTML;
/** @type {import('.').isVoidElement} */
function isVoidElement(tag) {
// Ordered by most common to least common.

@@ -136,13 +128,11 @@ return (

tag === 'wbr'
)
);
}
/**
* @type {import('.').styleToString}
*/
function styleToString (style) {
/** @type {import('.').styleToString} */
function styleToString(style) {
// Faster escaping process that only looks for the " character.
// As we use the " character to wrap the style string, we need to escape it.
if (typeof style === 'string') {
let end = style.indexOf('"')
let end = style.indexOf('"');

@@ -152,10 +142,11 @@ // This is a optimization to avoid having to look twice for the " character.

if (end === -1) {
return style
return style;
}
const length = style.length
const length = style.length;
let escaped = ''
let start = 0
let escaped = '';
let start = 0;
// Escapes double quotes to be used inside attributes
// Faster than using regex

@@ -165,4 +156,4 @@ // https://jsperf.app/kakihu

if (style[end] === '"') {
escaped += style.slice(start, end) + '&#34;'
start = end + 1
escaped += style.slice(start, end) + '&#34;';
start = end + 1;
}

@@ -172,34 +163,33 @@ }

// Appends the remaining string.
escaped += style.slice(start, end)
escaped += style.slice(start, end);
return escaped
return escaped;
}
const keys = Object.keys(style)
const length = keys.length
const keys = Object.keys(style);
const length = keys.length;
let key
let value
let index = 0
let result = ''
let key, value, end, start;
let index = 0;
let result = '';
for (; index < length; index++) {
key = keys[index]
key = keys[index];
// @ts-expect-error - this indexing is safe.
value = style[key]
value = style[key];
if (value === null || value === undefined) {
continue
continue;
}
// @ts-expect-error - this indexing is safe.
result += toKebabCase(key) + ':'
result += toKebabCase(key) + ':';
// Only needs escaping when the value is a string.
if (typeof value !== 'string') {
result += value.toString() + ';'
continue
result += value.toString() + ';';
continue;
}
let end = value.indexOf('"')
end = value.indexOf('"');

@@ -209,9 +199,10 @@ // This is a optimization to avoid having to look twice for the " character.

if (end === -1) {
result += value + ';'
continue
result += value + ';';
continue;
}
const length = value.length
let start = 0
const length = value.length;
start = 0;
// Escapes double quotes to be used inside attributes
// Faster than using regex

@@ -221,4 +212,4 @@ // https://jsperf.app/kakihu

if (value[end] === '"') {
result += value.slice(start, end) + '&#34;'
start = end + 1
result += value.slice(start, end) + '&#34;';
start = end + 1;
}

@@ -228,33 +219,27 @@ }

// Appends the remaining string.
result += value.slice(start, end) + ';'
result += value.slice(start, end) + ';';
}
return result
return result;
}
/**
* @type {import('.').attributesToString}
*/
function attributesToString (attributes) {
if (!attributes) {
return ''
}
/** @type {import('.').attributesToString} */
function attributesToString(attributes) {
const keys = Object.keys(attributes);
const length = keys.length;
const keys = Object.keys(attributes)
const length = keys.length
let key, value, type, end, start;
let result = '';
let index = 0;
let key, value, type
let result = ''
let index = 0
for (; index < length; index++) {
key = keys[index]
key = keys[index];
// Skips all @kitajs/html specific attributes.
if (key === 'children' || key === 'safe') {
continue
continue;
}
// @ts-expect-error - this indexing is safe.
value = attributes[key]
value = attributes[key];

@@ -265,14 +250,14 @@ // React className compatibility.

if (attributes.class !== undefined) {
continue
continue;
}
key = 'class'
key = 'class';
}
if (key === 'style') {
result += ' style="' + styleToString(value) + '"'
continue
result += ' style="' + styleToString(value) + '"';
continue;
}
type = typeof value
type = typeof value;

@@ -282,13 +267,13 @@ if (type === 'boolean') {

if (value) {
result += ' ' + key
result += ' ' + key;
}
continue
continue;
}
if (value === null || value === undefined) {
continue
continue;
}
result += ' ' + key
result += ' ' + key;

@@ -298,9 +283,9 @@ if (type !== 'string') {

if (type !== 'object') {
result += '="' + value.toString() + '"'
continue
result += '="' + value.toString() + '"';
continue;
// Dates are always safe
} else if (value instanceof Date) {
result += '="' + value.toISOString() + '"'
continue
result += '="' + value.toISOString() + '"';
continue;
}

@@ -310,6 +295,6 @@

// Which results in a non escaped string.
value = value.toString()
value = value.toString();
}
let end = value.indexOf('"')
end = value.indexOf('"');

@@ -319,11 +304,12 @@ // This is a optimization to avoid having to look twice for the " character.

if (end === -1) {
result += '="' + value + '"'
continue
result += '="' + value + '"';
continue;
}
result += '="'
result += '="';
const length = value.length
let start = 0
const length = value.length;
start = 0;
// Escapes double quotes to be used inside attributes
// Faster than using regex

@@ -333,4 +319,4 @@ // https://jsperf.app/kakihu

if (value[end] === '"') {
result += value.slice(start, end) + '&#34;'
start = end + 1
result += value.slice(start, end) + '&#34;';
start = end + 1;
}

@@ -340,6 +326,6 @@ }

// Appends the remaining string.
result += value.slice(start, end) + '"'
result += value.slice(start, end) + '"';
}
return result
return result;
}

@@ -349,32 +335,50 @@

* @type {import('.').contentsToString}
* @returns {any}
*/
function contentsToString (contents, escape) {
const length = contents.length
function contentsToString(contents, escape) {
const length = contents.length;
if (length === 0) {
return ''
}
let result = '';
let content;
let index = 0;
let result = ''
let content
let index = 0
for (; index < length; index++) {
content = contents[index]
content = contents[index];
// Ignores non 0 falsy values
if (!content && content !== 0) {
continue
if (typeof content !== 'string') {
// Ignores non 0 falsy values
if (!content && content !== 0) {
continue;
}
if (Array.isArray(content)) {
content = contentsToString(content);
}
// @ts-expect-error - Also accepts thenable objects, not only promises
// https://jsperf.app/zipuvi
if (content.then) {
// @ts-expect-error - this is a promise
return content.then(function resolveAsyncContent(resolved) {
return contentsToString(
[result, resolved]
// if we also pass escape here, it would double escape this result
// with the above call.
.concat(contents.slice(index + 1)),
escape
);
});
}
}
if (Array.isArray(content)) {
result += contentsToString(content, escape)
} else if (escape === true) {
result += escapeHtml(content)
} else {
result += content
}
result += content;
}
return result
// escapeHtml is faster with longer strings, that's
// why we escape the entire result once
if (escape === true) {
return escapeHtml(result);
}
return result;
}

@@ -384,10 +388,11 @@

* Just to stop TS from complaining about the type.
* @param {any} name
*
* @type {import('.').createElement}
* @param {any} name
* @returns {any}
*/
function createElement (name, attrs, ...children) {
function createElement(name, attrs, ...children) {
// Adds the children to the attributes if it is not present.
if (attrs === null) {
attrs = { children }
attrs = { children };
}

@@ -401,49 +406,51 @@

if (children.length > 1) {
attrs.children = children
attrs.children = children;
} else {
attrs.children = children[0]
attrs.children = children[0];
}
}
return name(attrs)
return name(attrs);
}
if (name === Fragment) {
return contentsToString(children)
}
// Switches the tag name when this custom `tag` is present.
if (name === 'tag') {
name = String(attrs.of)
delete attrs.of
name = String(attrs.of);
delete attrs.of;
}
if (children.length === 0 && isVoidElement(name)) {
return '<' + name + attributesToString(attrs) + '/>'
return '<' + name + attributesToString(attrs) + '/>';
}
return (
'<' +
name +
attributesToString(attrs) +
'>' +
contentsToString(children, attrs.safe) +
'</' +
name +
'>'
)
let contents = contentsToString(children, attrs.safe);
// Faster than checking if `children instanceof Promise`
// https://jsperf.app/zipuvi
if (typeof contents === 'string') {
return '<' + name + attributesToString(attrs) + '>' + contents + '</' + name + '>';
}
return contents.then(function asyncChildren(child) {
return '<' + name + attributesToString(attrs) + '>' + child + '</' + name + '>';
});
}
/** @type {import('.').Fragment} */
function Fragment(props) {
return Html.contentsToString([props.children]);
}
/**
* Just to stop TS from complaining about the type.
* @returns {Function}
*
* @type {import('.').compile}
* @returns {Function}
*/
function compile (htmlFn, strict = true, separator = '/*\x00*/') {
function compile(htmlFn, strict = true, separator = '/*\x00*/') {
if (typeof htmlFn !== 'function') {
throw new Error('The first argument must be a function.')
throw new Error('The first argument must be a function.');
}
const properties = new Set()
const properties = new Set();

@@ -455,12 +462,12 @@ const html = htmlFn(

{
get (_, name) {
get(_, name) {
// Adds the property to the set of known properties.
properties.add(name)
properties.add(name);
const isChildren = name === 'children'
let access = `args[${separator}\`${name.toString()}\`${separator}]`
const isChildren = name === 'children';
let access = `args[${separator}\`${name.toString()}\`${separator}]`;
// Adds support to render multiple children
if (isChildren) {
access = `Array.isArray(${access}) ? ${access}.join(${separator}\`\`${separator}) : ${access}`
access = `Array.isArray(${access}) ? ${access}.join(${separator}\`\`${separator}) : ${access}`;
}

@@ -473,15 +480,19 @@

: `${separator}\`\`${separator}`
}) + ${separator}\``
}) + ${separator}\``;
}
}
)
)
);
const sepLength = separator.length
const length = html.length
if (typeof html !== 'string') {
throw new Error('You cannot use compile() with async components.');
}
const sepLength = separator.length;
const length = html.length;
// Adds the throwPropertyNotFound function if strict
let body = ''
let nextStart = 0
let index = 0
let body = '';
let nextStart = 0;
let index = 0;

@@ -497,21 +508,12 @@ // Escapes every ` without separator

) {
body += html.slice(nextStart, index) + '\\`'
nextStart = index + 1
continue
body += html.slice(nextStart, index) + '\\`';
nextStart = index + 1;
continue;
}
// Escapes the backslash character because it will be used to escape the
// backtick character.
if (html[index] === '\\') {
body += html.slice(nextStart, index) + '\\\\'
nextStart = index + 1
continue
}
}
// Adds the remaining string
body += html.slice(nextStart)
body += html.slice(nextStart);
if (strict) {
// eslint-disable-next-line no-new-func
return Function(

@@ -525,28 +527,40 @@ 'args',

`return \`${body}\``
)
);
}
// eslint-disable-next-line no-new-func
return Function(
'args',
// Adds a empty args object when it is not present
'if (args === undefined) { args = Object.create(null) };\n' +
`return \`${body}\``
)
'if (args === undefined) { args = Object.create(null) };\n' + `return \`${body}\``
);
}
module.exports.escapeHtml = escapeHtml
module.exports.isVoidElement = isVoidElement
module.exports.attributesToString = attributesToString
module.exports.toKebabCase = toKebabCase
module.exports.isUpper = isUpper
module.exports.styleToString = styleToString
module.exports.createElement = createElement
module.exports.h = createElement
module.exports.contentsToString = contentsToString
module.exports.compile = compile
module.exports.Fragment = Fragment
const Html = {
escapeHtml,
isVoidElement,
attributesToString,
toKebabCase,
isUpper,
styleToString,
createElement,
h: createElement,
contentsToString,
compile,
Fragment
};
// esModule interop
Object.defineProperty(exports, '__esModule', { value: true })
module.exports.default = Object.assign({}, module.exports)
/**
* These export configurations enable JS and TS developers to consumer @kitajs/html in
* whatever way best suits their needs. Some examples of supported import syntax
* includes:
*
* - `const Html = require('@kitajs/html')`
* - `const { Html } = require('@kitajs/html')`
* - `import * as Fastify from '@kitajs/html'`
* - `import { Html, TSC_definition } from '@kitajs/html'`
* - `import Html from '@kitajs/html'`
* - `import Html, { TSC_definition } from '@kitajs/html'`
*/
module.exports = Html;
module.exports.Html = Html;
module.exports.default = Html;

@@ -7,8 +7,17 @@ // This file is a result from many sources, including: RFCs, typescript dom lib, w3schools, and others.

declare namespace JSX {
type Element = string
/**
* A {@linkcode JSX.Element} will always be a string, unless one of its children is a
* promise, in which case all of its subsequent children will also be promises.
*
* Direct calls of `Html.createElement` uses correct return type based on its children.
* However, when using JSX syntax, typescript does not support this yet.
*
* @see https://github.com/microsoft/TypeScript/issues/14729
*/
type Element = string | Promise<string>;
/**
* The index signature was removed to enable closed typing for style
* using CSSType. You're able to use type assertion or module augmentation
* to add properties or an index signature of your own.
* The index signature was removed to enable closed typing for style using CSSType.
* You're able to use type assertion or module augmentation to add properties or an
* index signature of your own.
*

@@ -18,45 +27,36 @@ * For examples and more information, visit:

*/
type CSSProperties = import('csstype').Properties<string | number | boolean>
type CSSProperties = import('csstype').Properties<string | number | boolean>;
interface HtmlTag extends ElementChildrenAttribute, IntrinsicAttributes {
accesskey?: undefined | string
class?: undefined | string
contenteditable?: undefined | string
dir?: undefined | string
hidden?: undefined | string | boolean
id?: undefined | string
role?: undefined | string
lang?: undefined | string
draggable?: undefined | string | boolean
spellcheck?: undefined | string | boolean
tabindex?: undefined | number | string
title?: undefined | string
translate?: undefined | string | boolean
accesskey?: undefined | string;
class?: undefined | string;
contenteditable?: undefined | string;
dir?: undefined | string;
hidden?: undefined | string | boolean;
id?: undefined | string;
role?: undefined | string;
lang?: undefined | string;
draggable?: undefined | string | boolean;
spellcheck?: undefined | string | boolean;
tabindex?: undefined | number | string;
title?: undefined | string;
translate?: undefined | string | boolean;
/**
* A css style attribute which also supports a `csstype` object.
*/
style?: undefined | string | CSSProperties
/** A css style attribute which also supports a `csstype` object. */
style?: undefined | string | CSSProperties;
/**
* Tells if any inner html should be escaped.
* When set to true, all inner content (html or not) of this tag will be escaped when
* evaluated.
*
* **Warning: This also escapes inner jsx tags. You should only use this in the inner tag.**
* **Warning: This will escape even inner jsx tags. You should only use this in the
* most inner tag of the html tree.**
*
* @example
* ```tsx
* <div>{'<script />'}</div>
* '<div><script /></div>'
* @example ;```tsx <div>{'<script />'}</div> '<div><script /></div>' <div
* safe>{'<script />'}</div> '<div><script /></div>' <div><div>{'<script
* />'}</div></div> '<div><div><script /></div></div>'
*
* <div safe>{'<script />'}</div>
* '<div>&lt;script /&gt;</div>'
*
* <div><div>{'<script />'}</div></div>
* // Escapes even inner jsx tags <div safe><div>{'<script />'}</div></div>
* '<div><div><script /></div></div>'
*
* // Escapes even inner jsx tags
* <div safe><div>{'<script />'}</div></div>
* '<div>&lt;div&gt;&lt;script /&gt;&lt;/div&gt;</div>'
* ```
*
* @default false

@@ -66,3 +66,3 @@ *

*/
safe?: undefined | boolean
safe?: undefined | boolean;

@@ -72,49 +72,49 @@ /**

*
* @deprecated please use `class`.
* @deprecated Please use `class`.
*/
className?: undefined | string
className?: undefined | string;
}
interface HtmlAnchorTag extends HtmlTag {
href?: undefined | string
hreflang?: undefined | string
target?: undefined | string
download?: undefined | string
referrerpolicy?: undefined | string
ping?: undefined | string
rel?: undefined | string
media?: undefined | string
type?: undefined | string
href?: undefined | string;
hreflang?: undefined | string;
target?: undefined | string;
download?: undefined | string;
referrerpolicy?: undefined | string;
ping?: undefined | string;
rel?: undefined | string;
media?: undefined | string;
type?: undefined | string;
}
interface HtmlAreaTag extends HtmlTag {
alt?: undefined | string
coords?: undefined | string
shape?: undefined | string
href?: undefined | string
target?: undefined | string
ping?: undefined | string
rel?: undefined | string
media?: undefined | string
hreflang?: undefined | string
type?: undefined | string
alt?: undefined | string;
coords?: undefined | string;
shape?: undefined | string;
href?: undefined | string;
target?: undefined | string;
ping?: undefined | string;
rel?: undefined | string;
media?: undefined | string;
hreflang?: undefined | string;
type?: undefined | string;
}
interface HtmlAudioTag extends HtmlTag {
src?: undefined | string
autobuffer?: undefined | string
autoplay?: undefined | string | boolean
preload?: undefined | string
muted?: undefined | string | boolean
loop?: undefined | string | boolean
controls?: undefined | string
src?: undefined | string;
autobuffer?: undefined | string;
autoplay?: undefined | string | boolean;
preload?: undefined | string;
muted?: undefined | string | boolean;
loop?: undefined | string | boolean;
controls?: undefined | string;
}
interface BaseTag extends HtmlTag {
href?: undefined | string
target?: undefined | string
href?: undefined | string;
target?: undefined | string;
}
interface HtmlQuoteTag extends HtmlTag {
cite?: undefined | string
cite?: undefined | string;
}

@@ -125,13 +125,13 @@

interface HtmlButtonTag extends HtmlTag {
action?: undefined | string
autofocus?: undefined | string
disabled?: undefined | boolean
enctype?: undefined | string
form?: undefined | string
method?: undefined | string
name?: undefined | string
novalidate?: undefined | string | boolean
target?: undefined | string
type?: undefined | string
value?: undefined | string
action?: undefined | string;
autofocus?: undefined | string;
disabled?: undefined | boolean;
enctype?: undefined | string;
form?: undefined | string;
method?: undefined | string;
name?: undefined | string;
novalidate?: undefined | string | boolean;
target?: undefined | string;
type?: undefined | string;
value?: undefined | string;
}

@@ -142,8 +142,8 @@

interface HtmlCanvasTag extends HtmlTag {
width?: undefined | string
height?: undefined | string
width?: undefined | string;
height?: undefined | string;
}
interface HtmlTableColTag extends HtmlTag {
span?: undefined | string
span?: undefined | string;
}

@@ -156,190 +156,190 @@

interface DataTag extends HtmlTag {
value?: undefined | string
value?: undefined | string;
}
interface HtmlEmbedTag extends HtmlTag, Record<string, any> {
src?: undefined | string
type?: undefined | string
width?: undefined | string
height?: undefined | string
src?: undefined | string;
type?: undefined | string;
width?: undefined | string;
height?: undefined | string;
}
interface HtmlFieldSetTag extends HtmlTag {
disabled?: undefined | boolean
form?: undefined | string
name?: undefined | string
disabled?: undefined | boolean;
form?: undefined | string;
name?: undefined | string;
}
interface HtmlFormTag extends HtmlTag {
['accept-charset']?: undefined | string
action?: undefined | string
autocomplete?: undefined | string
enctype?: undefined | string
method?: undefined | string
name?: undefined | string
novalidate?: undefined | string | boolean
target?: undefined | string
['accept-charset']?: undefined | string;
action?: undefined | string;
autocomplete?: undefined | string;
enctype?: undefined | string;
method?: undefined | string;
name?: undefined | string;
novalidate?: undefined | string | boolean;
target?: undefined | string;
}
interface HtmlHtmlTag extends HtmlTag {
manifest?: undefined | string
manifest?: undefined | string;
}
interface HtmlIFrameTag extends HtmlTag {
src?: undefined | string
srcdoc?: undefined | string
name?: undefined | string
sandbox?: undefined | string
seamless?: undefined | string
width?: undefined | string
height?: undefined | string
src?: undefined | string;
srcdoc?: undefined | string;
name?: undefined | string;
sandbox?: undefined | string;
seamless?: undefined | string;
width?: undefined | string;
height?: undefined | string;
}
interface HtmlImageTag extends HtmlTag {
alt?: undefined | string
src?: undefined | string
crossorigin?: undefined | string
usemap?: undefined | string
ismap?: undefined | string
width?: undefined | string
height?: undefined | string
alt?: undefined | string;
src?: undefined | string;
crossorigin?: undefined | string;
usemap?: undefined | string;
ismap?: undefined | string;
width?: undefined | number | string;
height?: undefined | number | string;
}
interface HtmlInputTag extends HtmlTag {
accept?: undefined | string
action?: undefined | string
alt?: undefined | string
autocomplete?: undefined | string
autofocus?: undefined | string
checked?: undefined | string | boolean
disabled?: undefined | boolean
enctype?: undefined | string
form?: undefined | string
height?: undefined | string
list?: undefined | string
max?: undefined | string
maxlength?: undefined | string
method?: undefined | string
min?: undefined | string
multiple?: undefined | string
name?: undefined | string
novalidate?: undefined | string | boolean
pattern?: undefined | string
placeholder?: undefined | string
readonly?: undefined | string
required?: undefined | string
size?: undefined | string
src?: undefined | string
step?: undefined | string
target?: undefined | string
type?: undefined | string
value?: undefined | string
width?: undefined | string
accept?: undefined | string;
action?: undefined | string;
alt?: undefined | string;
autocomplete?: undefined | string;
autofocus?: undefined | string;
checked?: undefined | string | boolean;
disabled?: undefined | boolean;
enctype?: undefined | string;
form?: undefined | string;
height?: undefined | string;
list?: undefined | string;
max?: undefined | string;
maxlength?: undefined | string;
method?: undefined | string;
min?: undefined | string;
multiple?: undefined | string;
name?: undefined | string;
novalidate?: undefined | string | boolean;
pattern?: undefined | string;
placeholder?: undefined | string;
readonly?: undefined | string;
required?: undefined | string;
size?: undefined | string;
src?: undefined | string;
step?: undefined | string;
target?: undefined | string;
type?: undefined | string;
value?: undefined | string;
width?: undefined | string;
}
interface HtmlModTag extends HtmlTag {
cite?: undefined | string
datetime?: undefined | string | Date
cite?: undefined | string;
datetime?: undefined | string | Date;
}
interface KeygenTag extends HtmlTag {
autofocus?: undefined | string
challenge?: undefined | string
disabled?: undefined | boolean
form?: undefined | string
keytype?: undefined | string
name?: undefined | string
autofocus?: undefined | string;
challenge?: undefined | string;
disabled?: undefined | boolean;
form?: undefined | string;
keytype?: undefined | string;
name?: undefined | string;
}
interface HtmlLabelTag extends HtmlTag {
form?: undefined | string
for?: undefined | string
form?: undefined | string;
for?: undefined | string;
}
interface HtmlLITag extends HtmlTag {
value?: undefined | string | number
value?: undefined | string | number;
}
interface HtmlLinkTag extends HtmlTag {
href?: undefined | string
crossorigin?: undefined | string
rel?: undefined | string
media?: undefined | string
hreflang?: undefined | string
type?: undefined | string
sizes?: undefined | string
integrity?: undefined | string
href?: undefined | string;
crossorigin?: undefined | string;
rel?: undefined | string;
media?: undefined | string;
hreflang?: undefined | string;
type?: undefined | string;
sizes?: undefined | string;
integrity?: undefined | string;
}
interface HtmlMapTag extends HtmlTag {
name?: undefined | string
name?: undefined | string;
}
interface HtmlMetaTag extends HtmlTag {
name?: undefined | string
['http-equiv']?: undefined | string
content?: undefined | string
charset?: undefined | string
name?: undefined | string;
['http-equiv']?: undefined | string;
content?: undefined | string;
charset?: undefined | string;
}
interface HtmlMeterTag extends HtmlTag {
value?: undefined | string | number
min?: undefined | string | number
max?: undefined | string | number
low?: undefined | string | number
high?: undefined | string | number
optimum?: undefined | string | number
value?: undefined | string | number;
min?: undefined | string | number;
max?: undefined | string | number;
low?: undefined | string | number;
high?: undefined | string | number;
optimum?: undefined | string | number;
}
interface HtmlObjectTag extends HtmlTag {
data?: undefined | string
type?: undefined | string
name?: undefined | string
usemap?: undefined | string
form?: undefined | string
width?: undefined | string
height?: undefined | string
data?: undefined | string;
type?: undefined | string;
name?: undefined | string;
usemap?: undefined | string;
form?: undefined | string;
width?: undefined | string;
height?: undefined | string;
}
interface HtmlOListTag extends HtmlTag {
reversed?: undefined | string
start?: undefined | string | number
reversed?: undefined | string;
start?: undefined | string | number;
}
interface HtmlOptgroupTag extends HtmlTag {
disabled?: undefined | boolean
label?: undefined | string
disabled?: undefined | boolean;
label?: undefined | string;
}
interface HtmlOptionTag extends HtmlTag {
disabled?: undefined | boolean
label?: undefined | string
selected?: undefined | string
value?: undefined | string
disabled?: undefined | boolean;
label?: undefined | string;
selected?: undefined | string;
value?: undefined | string;
}
interface HtmlOutputTag extends HtmlTag {
for?: undefined | string
form?: undefined | string
name?: undefined | string
for?: undefined | string;
form?: undefined | string;
name?: undefined | string;
}
interface HtmlParamTag extends HtmlTag {
name?: undefined | string
value?: undefined | string
name?: undefined | string;
value?: undefined | string;
}
interface HtmlProgressTag extends HtmlTag {
value?: undefined | string | number
max?: undefined | string | number
value?: undefined | string | number;
max?: undefined | string | number;
}
interface HtmlCommandTag extends HtmlTag {
type?: undefined | string
label?: undefined | string
icon?: undefined | string
disabled?: undefined | boolean
checked?: undefined | string
radiogroup?: undefined | string
default?: undefined | string
type?: undefined | string;
label?: undefined | string;
icon?: undefined | string;
disabled?: undefined | boolean;
checked?: undefined | string;
radiogroup?: undefined | string;
default?: undefined | string;
}

@@ -350,46 +350,46 @@

interface HtmlBrowserButtonTag extends HtmlTag {
type?: undefined | string
type?: undefined | string;
}
interface HtmlMenuTag extends HtmlTag {
type?: undefined | string
label?: undefined | string
type?: undefined | string;
label?: undefined | string;
}
interface HtmlScriptTag extends HtmlTag {
src?: undefined | string
type?: undefined | string
charset?: undefined | string
async?: undefined | string | boolean
defer?: undefined | string | boolean
crossorigin?: undefined | string
integrity?: undefined | string
text?: undefined | string
src?: undefined | string;
type?: undefined | string;
charset?: undefined | string;
async?: undefined | string | boolean;
defer?: undefined | string | boolean;
crossorigin?: undefined | string;
integrity?: undefined | string;
text?: undefined | string;
}
interface HtmlDetailsTag extends HtmlTag {
open?: undefined | string
open?: undefined | string;
}
interface HtmlSelectTag extends HtmlTag {
autofocus?: undefined | string
disabled?: undefined | boolean
form?: undefined | string
multiple?: undefined | string
name?: undefined | string
required?: undefined | string
size?: undefined | string
autofocus?: undefined | string;
disabled?: undefined | boolean;
form?: undefined | string;
multiple?: undefined | string;
name?: undefined | string;
required?: undefined | string;
size?: undefined | string;
}
interface HtmlSourceTag extends HtmlTag {
src?: undefined | string
type?: undefined | string
media?: undefined | string
src?: undefined | string;
type?: undefined | string;
media?: undefined | string;
}
interface HtmlStyleTag extends HtmlTag {
media?: undefined | string
type?: undefined | string
disabled?: undefined | boolean
scoped?: undefined | string
media?: undefined | string;
type?: undefined | string;
disabled?: undefined | boolean;
scoped?: undefined | string;
}

@@ -400,51 +400,51 @@

interface HtmlTableDataCellTag extends HtmlTag {
colspan?: undefined | string | number
rowspan?: undefined | string | number
headers?: undefined | string
colspan?: undefined | string | number;
rowspan?: undefined | string | number;
headers?: undefined | string;
}
interface HtmlTextAreaTag extends HtmlTag {
autofocus?: undefined | string
cols?: undefined | string
dirname?: undefined | string
disabled?: undefined | boolean
form?: undefined | string
maxlength?: undefined | string
minlength?: undefined | string
name?: undefined | string
placeholder?: undefined | string
readonly?: undefined | string
required?: undefined | string
rows?: undefined | string
wrap?: undefined | string
autofocus?: undefined | string;
cols?: undefined | string;
dirname?: undefined | string;
disabled?: undefined | boolean;
form?: undefined | string;
maxlength?: undefined | string;
minlength?: undefined | string;
name?: undefined | string;
placeholder?: undefined | string;
readonly?: undefined | string;
required?: undefined | string;
rows?: undefined | string;
wrap?: undefined | string;
}
interface HtmlTableHeaderCellTag extends HtmlTag {
colspan?: undefined | string | number
rowspan?: undefined | string | number
headers?: undefined | string
scope?: undefined | string
colspan?: undefined | string | number;
rowspan?: undefined | string | number;
headers?: undefined | string;
scope?: undefined | string;
}
interface HtmlTimeTag extends HtmlTag {
datetime?: undefined | string | Date
datetime?: undefined | string | Date;
}
interface HtmlTrackTag extends HtmlTag {
default?: undefined | string
kind?: undefined | string
label?: undefined | string
src?: undefined | string
srclang?: undefined | string
default?: undefined | string;
kind?: undefined | string;
label?: undefined | string;
src?: undefined | string;
srclang?: undefined | string;
}
interface HtmlVideoTag extends HtmlTag {
src?: undefined | string
poster?: undefined | string
autobuffer?: undefined | string
autoplay?: undefined | string
loop?: undefined | string
controls?: undefined | string
width?: undefined | string
height?: undefined | string
src?: undefined | string;
poster?: undefined | string;
autobuffer?: undefined | string;
autoplay?: undefined | string;
loop?: undefined | string;
controls?: undefined | string;
width?: undefined | string;
height?: undefined | string;
}

@@ -456,60 +456,60 @@

interface HtmlUnspecifiedTag extends HtmlTag, Record<string, any> {
of: string
of: string;
}
interface HtmlBodyTag {
onafterprint?: undefined | string
onbeforeprint?: undefined | string
onbeforeonload?: undefined | string
onblur?: undefined | string
onerror?: undefined | string
onfocus?: undefined | string
onhaschange?: undefined | string
onload?: undefined | string
onmessage?: undefined | string
onoffline?: undefined | string
ononline?: undefined | string
onpagehide?: undefined | string
onpageshow?: undefined | string
onpopstate?: undefined | string
onredo?: undefined | string
onresize?: undefined | string
onstorage?: undefined | string
onundo?: undefined | string
onunload?: undefined | string
onafterprint?: undefined | string;
onbeforeprint?: undefined | string;
onbeforeonload?: undefined | string;
onblur?: undefined | string;
onerror?: undefined | string;
onfocus?: undefined | string;
onhaschange?: undefined | string;
onload?: undefined | string;
onmessage?: undefined | string;
onoffline?: undefined | string;
ononline?: undefined | string;
onpagehide?: undefined | string;
onpageshow?: undefined | string;
onpopstate?: undefined | string;
onredo?: undefined | string;
onresize?: undefined | string;
onstorage?: undefined | string;
onundo?: undefined | string;
onunload?: undefined | string;
}
interface HtmlTag {
oncontextmenu?: undefined | string
onkeydown?: undefined | string
onkeypress?: undefined | string
onkeyup?: undefined | string
onclick?: undefined | string
ondblclick?: undefined | string
ondrag?: undefined | string
ondragend?: undefined | string
ondragenter?: undefined | string
ondragleave?: undefined | string
ondragover?: undefined | string
ondragstart?: undefined | string
ondrop?: undefined | string
onmousedown?: undefined | string
onmousemove?: undefined | string
onmouseout?: undefined | string
onmouseover?: undefined | string
onmouseup?: undefined | string
onmousewheel?: undefined | string
onscroll?: undefined | string
oncontextmenu?: undefined | string;
onkeydown?: undefined | string;
onkeypress?: undefined | string;
onkeyup?: undefined | string;
onclick?: undefined | string;
ondblclick?: undefined | string;
ondrag?: undefined | string;
ondragend?: undefined | string;
ondragenter?: undefined | string;
ondragleave?: undefined | string;
ondragover?: undefined | string;
ondragstart?: undefined | string;
ondrop?: undefined | string;
onmousedown?: undefined | string;
onmousemove?: undefined | string;
onmouseout?: undefined | string;
onmouseover?: undefined | string;
onmouseup?: undefined | string;
onmousewheel?: undefined | string;
onscroll?: undefined | string;
}
interface FormEvents {
onblur?: undefined | string
onchange?: undefined | string
onfocus?: undefined | string
onformchange?: undefined | string
onforminput?: undefined | string
oninput?: undefined | string
oninvalid?: undefined | string
onselect?: undefined | string
onsubmit?: undefined | string
onblur?: undefined | string;
onchange?: undefined | string;
onfocus?: undefined | string;
onformchange?: undefined | string;
onforminput?: undefined | string;
oninput?: undefined | string;
oninvalid?: undefined | string;
onselect?: undefined | string;
onsubmit?: undefined | string;
}

@@ -524,25 +524,25 @@

interface MediaEvents {
onabort?: undefined | string
oncanplay?: undefined | string
oncanplaythrough?: undefined | string
ondurationchange?: undefined | string
onemptied?: undefined | string
onended?: undefined | string
onerror?: undefined | string
onloadeddata?: undefined | string
onloadedmetadata?: undefined | string
onloadstart?: undefined | string
onpause?: undefined | string
onplay?: undefined | string
onplaying?: undefined | string
onprogress?: undefined | string
onratechange?: undefined | string
onreadystatechange?: undefined | string
onseeked?: undefined | string
onseeking?: undefined | string
onstalled?: undefined | string
onsuspend?: undefined | string
ontimeupdate?: undefined | string
onvolumechange?: undefined | string
onwaiting?: undefined | string
onabort?: undefined | string;
oncanplay?: undefined | string;
oncanplaythrough?: undefined | string;
ondurationchange?: undefined | string;
onemptied?: undefined | string;
onended?: undefined | string;
onerror?: undefined | string;
onloadeddata?: undefined | string;
onloadedmetadata?: undefined | string;
onloadstart?: undefined | string;
onpause?: undefined | string;
onplay?: undefined | string;
onplaying?: undefined | string;
onprogress?: undefined | string;
onratechange?: undefined | string;
onreadystatechange?: undefined | string;
onseeked?: undefined | string;
onseeking?: undefined | string;
onstalled?: undefined | string;
onsuspend?: undefined | string;
ontimeupdate?: undefined | string;
onvolumechange?: undefined | string;
onwaiting?: undefined | string;
}

@@ -563,123 +563,181 @@

interface ElementChildrenAttribute {
children?: undefined | any
children?: undefined | any;
}
interface IntrinsicElements {
a: HtmlAnchorTag
abbr: HtmlTag
address: HtmlTag
area: HtmlAreaTag
article: HtmlTag
aside: HtmlTag
audio: HtmlAudioTag
b: HtmlTag
bb: HtmlBrowserButtonTag
base: BaseTag
bdi: HtmlTag
bdo: HtmlTag
blockquote: HtmlQuoteTag
body: HtmlBodyTag
br: HtmlTag
button: HtmlButtonTag
canvas: HtmlCanvasTag
caption: HtmlTag
cite: HtmlTag
code: HtmlTag
col: HtmlTableColTag
colgroup: HtmlTableColTag
commands: HtmlCommandTag
data: DataTag
datalist: HtmlDataListTag
dd: HtmlTag
del: HtmlModTag
details: HtmlDetailsTag
dfn: HtmlTag
div: HtmlTag
dl: HtmlTag
dt: HtmlTag
em: HtmlTag
embed: HtmlEmbedTag
fieldset: HtmlFieldSetTag
figcaption: HtmlTag
figure: HtmlTag
footer: HtmlTag
form: HtmlFormTag
hgroup: HtmlTag
h1: HtmlTag
h2: HtmlTag
h3: HtmlTag
h4: HtmlTag
h5: HtmlTag
h6: HtmlTag
head: HtmlTag
header: HtmlTag
hr: HtmlTag
html: HtmlHtmlTag
i: HtmlTag
iframe: HtmlIFrameTag
img: HtmlImageTag
input: HtmlInputTag
ins: HtmlModTag
kbd: HtmlTag
keygen: KeygenTag
label: HtmlLabelTag
legend: HtmlLegendTag
li: HtmlLITag
link: HtmlLinkTag
main: HtmlTag
map: HtmlMapTag
mark: HtmlTag
menu: HtmlMenuTag
meta: HtmlMetaTag
meter: HtmlMeterTag
nav: HtmlTag
noscript: HtmlTag
object: HtmlObjectTag
ol: HtmlOListTag
optgroup: HtmlOptgroupTag
option: HtmlOptionTag
output: HtmlOutputTag
p: HtmlTag
param: HtmlParamTag
pre: HtmlTag
progress: HtmlProgressTag
q: HtmlQuoteTag
rb: HtmlTag
rp: HtmlTag
rt: HtmlTag
rtc: HtmlTag
ruby: HtmlTag
s: HtmlTag
samp: HtmlTag
script: HtmlScriptTag
section: HtmlTag
select: HtmlSelectTag
small: HtmlTag
summary: HtmlTag
source: HtmlSourceTag
span: HtmlTag
strong: HtmlTag
style: HtmlStyleTag
sub: HtmlTag
sup: HtmlTag
svg: HtmlSvgTag
table: HtmlTableTag
tbody: HtmlTag
td: HtmlTableDataCellTag
template: HtmlTag
textarea: HtmlTextAreaTag
tfoot: HtmlTableSectionTag
th: HtmlTableHeaderCellTag
thead: HtmlTableSectionTag
time: HtmlTimeTag
title: HtmlTag
tr: HtmlTableRowTag
track: HtmlTrackTag
u: HtmlTag
ul: HtmlTag
var: HtmlTag
video: HtmlVideoTag
wbr: HtmlTag
tag: HtmlUnspecifiedTag
a: HtmlAnchorTag;
abbr: HtmlTag;
address: HtmlTag;
animate: HtmlSvgTag;
animateMotion: HtmlSvgTag;
animateTransform: HtmlSvgTag;
area: HtmlAreaTag;
article: HtmlTag;
aside: HtmlTag;
audio: HtmlAudioTag;
b: HtmlTag;
base: BaseTag;
bb: HtmlBrowserButtonTag;
bdi: HtmlTag;
bdo: HtmlTag;
blockquote: HtmlQuoteTag;
body: HtmlBodyTag;
br: HtmlTag;
button: HtmlButtonTag;
canvas: HtmlCanvasTag;
caption: HtmlTag;
circle: HtmlSvgTag;
cite: HtmlTag;
clipPath: HtmlSvgTag;
code: HtmlTag;
col: HtmlTableColTag;
colgroup: HtmlTableColTag;
commands: HtmlCommandTag;
data: DataTag;
datalist: HtmlDataListTag;
dd: HtmlTag;
defs: HtmlSvgTag;
del: HtmlModTag;
desc: HtmlSvgTag;
details: HtmlDetailsTag;
dfn: HtmlTag;
div: HtmlTag;
dl: HtmlTag;
dt: HtmlTag;
ellipse: HtmlSvgTag;
em: HtmlTag;
embed: HtmlEmbedTag;
feBlend: HtmlSvgTag;
feColorMatrix: HtmlSvgTag;
feComponentTransfer: HtmlSvgTag;
feComposite: HtmlSvgTag;
feConvolveMatrix: HtmlSvgTag;
feDiffuseLighting: HtmlSvgTag;
feDisplacementMap: HtmlSvgTag;
feDistantLight: HtmlSvgTag;
feDropShadow: HtmlSvgTag;
feFlood: HtmlSvgTag;
feFuncA: HtmlSvgTag;
feFuncB: HtmlSvgTag;
feFuncG: HtmlSvgTag;
feFuncR: HtmlSvgTag;
feGaussianBlur: HtmlSvgTag;
feImage: HtmlSvgTag;
feMerge: HtmlSvgTag;
feMergeNode: HtmlSvgTag;
feMorphology: HtmlSvgTag;
feOffset: HtmlSvgTag;
fePointLight: HtmlSvgTag;
feSpecularLighting: HtmlSvgTag;
feSpotLight: HtmlSvgTag;
feTile: HtmlSvgTag;
feTurbulence: HtmlSvgTag;
fieldset: HtmlFieldSetTag;
figcaption: HtmlTag;
figure: HtmlTag;
filter: HtmlSvgTag;
footer: HtmlTag;
foreignObject: HtmlSvgTag;
form: HtmlFormTag;
g: HtmlSvgTag;
h1: HtmlTag;
h2: HtmlTag;
h3: HtmlTag;
h4: HtmlTag;
h5: HtmlTag;
h6: HtmlTag;
head: HtmlTag;
header: HtmlTag;
hgroup: HtmlTag;
hr: HtmlTag;
html: HtmlHtmlTag;
i: HtmlTag;
iframe: HtmlIFrameTag;
image: HtmlSvgTag;
img: HtmlImageTag;
input: HtmlInputTag;
ins: HtmlModTag;
kbd: HtmlTag;
keygen: KeygenTag;
label: HtmlLabelTag;
legend: HtmlLegendTag;
li: HtmlLITag;
line: HtmlSvgTag;
linearGradient: HtmlSvgTag;
link: HtmlLinkTag;
main: HtmlTag;
map: HtmlMapTag;
mark: HtmlTag;
marker: HtmlSvgTag;
mask: HtmlSvgTag;
menu: HtmlMenuTag;
meta: HtmlMetaTag;
metadata: HtmlSvgTag;
meter: HtmlMeterTag;
mpath: HtmlSvgTag;
nav: HtmlTag;
noscript: HtmlTag;
object: HtmlObjectTag;
ol: HtmlOListTag;
optgroup: HtmlOptgroupTag;
option: HtmlOptionTag;
output: HtmlOutputTag;
p: HtmlTag;
param: HtmlParamTag;
path: HtmlSvgTag;
pattern: HtmlSvgTag;
polygon: HtmlSvgTag;
polyline: HtmlSvgTag;
pre: HtmlTag;
progress: HtmlProgressTag;
q: HtmlQuoteTag;
radialGradient: HtmlSvgTag;
rb: HtmlTag;
rect: HtmlSvgTag;
rp: HtmlTag;
rt: HtmlTag;
rtc: HtmlTag;
ruby: HtmlTag;
s: HtmlTag;
samp: HtmlTag;
script: HtmlScriptTag;
section: HtmlTag;
select: HtmlSelectTag;
set: HtmlSvgTag;
small: HtmlTag;
source: HtmlSourceTag;
span: HtmlTag;
stop: HtmlSvgTag;
strong: HtmlTag;
style: HtmlStyleTag;
sub: HtmlTag;
summary: HtmlTag;
sup: HtmlTag;
svg: HtmlSvgTag;
switch: HtmlSvgTag;
symbol: HtmlSvgTag;
table: HtmlTableTag;
tag: HtmlUnspecifiedTag;
tbody: HtmlTag;
td: HtmlTableDataCellTag;
template: HtmlTag;
text: HtmlSvgTag;
textarea: HtmlTextAreaTag;
textPath: HtmlSvgTag;
tfoot: HtmlTableSectionTag;
th: HtmlTableHeaderCellTag;
thead: HtmlTableSectionTag;
time: HtmlTimeTag;
title: HtmlTag;
tr: HtmlTableRowTag;
track: HtmlTrackTag;
tspan: HtmlSvgTag;
u: HtmlTag;
ul: HtmlTag;
use: HtmlSvgTag;
var: HtmlTag;
video: HtmlVideoTag;
view: HtmlSvgTag;
wbr: HtmlTag;
}
}
{
"name": "@kitajs/html",
"version": "2.2.2",
"version": "3.0.0",
"description": "Fast and type safe HTML templates using TypeScript.",
"main": "index.js",
"types": "index.d.ts",
"bugs": "https://github.com/kitajs/html/issues",
"repository": "https://github.com/kitajs/html",
"bugs": "https://github.com/kitajs/html/issues",
"funding": "https://github.com/kitajs/html?sponsor=1",
"license": "Apache-2.0",
"author": "arthurfiorette <npm@arthur.place>",
"license": "MIT",
"sideEffects": false,
"main": "index.js",
"types": "index.d.ts",
"dependencies": {

@@ -17,6 +17,7 @@ "csstype": "^3.1.2"

"devDependencies": {
"@arthurfiorette/prettier-config": "^1.0.9",
"@types/node": "^20.4.2",
"benny": "^3.7.1",
"c8": "^8.0.1",
"prettier": "^3.0.0",
"standard": "^17.1.0",
"tslib": "^2.6.1",

@@ -26,2 +27,5 @@ "typed-html": "^3.0.1",

},
"peerDependencies": {
"@kitajs/ts-html-plugin": ">=1.1"
},
"packageManager": "pnpm@8.7.5",

@@ -32,7 +36,7 @@ "engines": {

"scripts": {
"test": "tsc && node --test dist/test",
"bench": "tsc && node --expose-gc dist/benchmark",
"format": "prettier --write .",
"lint": "standard"
"test": "tsc && c8 --reporter lcov --reporter text node --trace-warnings --test dist/test",
"test:only": "tsc; node --trace-warnings --test-only --test dist/test"
}
}

@@ -0,46 +1,55 @@

<p align="center">
<b>Using this package?</b> Please consider <a href="https://github.com/sponsors/arthurfiorette" target="_blank">donating</a> to support my open source work ❤️
<br />
<sup>
Help @kitajs/html grow! Star and share this amazing repository with your friends and co-workers!
</sup>
</p>
<br />
[![Issues](https://img.shields.io/github/issues/kitajs/html?logo=github&label=Issues)](https://github.com/kitajs/html/issues)
[![Stars](https://img.shields.io/github/stars/kitajs/html?logo=github&label=Stars)](https://github.com/kitajs/html/stargazers)
[![License](https://img.shields.io/github/license/kitajs/html?logo=githu&label=License)](https://github.com/kitajs/html/blob/master/LICENSE)
[![Speed Blazing](https://img.shields.io/badge/speed-blazing%20%F0%9F%94%A5-brightgreen.svg)](https://twitter.com/acdlite/status/974390255393505280)
<p align="center" >
<a href="https://kita.js.org" target="_blank" rel="noopener noreferrer">
<img src="https://kita.js.org/logo.png" width="180" alt="Kita JS logo" />
</a>
</p>
[![Latest Version](https://img.shields.io/npm/v/@kitajs/html)](https://www.npmjs.com/package/@kitajs/html)
[![Downloads](https://img.shields.io/npm/dw/@kitajs/html)](https://www.npmjs.com/package/@kitajs/html)
[![JsDelivr](https://data.jsdelivr.com/v1/package/npm/@kitajs/html/badge?style=rounded)](https://www.jsdelivr.com/package/npm/@kitajs/html)
[![Bundlephobia](https://img.shields.io/bundlephobia/minzip/@kitajs/html/latest?style=flat)](https://bundlephobia.com/package/@kitajs/html@latest)
[![Packagephobia](https://packagephobia.com/badge?p=@kitajs/html@latest)](https://packagephobia.com/result?p=@kitajs/html@latest)
<br />
<div align="center">
<pre>
<h1>🏛️<br />KitaJS Html</h1>
</pre>
<br />
<a title="MIT license" target="_blank" href="https://github.com/kitajs/html/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/github/license/kitajs/html"></a>
<a title="Codecov" target="_blank" href="https://app.codecov.io/gh/kitajs/html"><img alt="Codecov" src="https://img.shields.io/codecov/c/github/kitajs/html?token=ML0KGCU0VM"></a>
<a title="NPM Package" target="_blank" href="https://www.npmjs.com/package/@kitajs/html"><img alt="Downloads" src="https://img.shields.io/npm/dw/@kitajs/html?style=flat"></a>
<a title="Bundle size" target="_blank" href="https://bundlephobia.com/package/@kitajs/html@latest"><img alt="Bundlephobia" src="https://img.shields.io/bundlephobia/minzip/@kitajs/html/latest?style=flat"></a>
<a title="Last Commit" target="_blank" href="https://github.com/kitajs/html/commits/master"><img alt="Last commit" src="https://img.shields.io/github/last-commit/kitajs/html"></a>
<a href="https://github.com/kitajs/html/stargazers"><img src="https://img.shields.io/github/stars/kitajs/html?logo=github&label=Stars" alt="Stars"></a>
</div>
<h3 align="center">
<br />
<br />
<h1>🏛️ KitaJS Html</h1>
<p align="center">
<code>@kitajs/html</code> is a no dependencies, fast and concise package to generate HTML through JavaScript with JSX syntax.
<br />
<br />
</h3>
</p>
<br />
> [!WARNING]
> Learn how to [sanitize](#sanitization) and avoid [xss](https://owasp.org/www-community/attacks/xss) vulnerabilities in your code!
<br />
## Table of Contents
- [Table of Contents](#table-of-contents)
- [Preview](#preview)
- [Installing](#installing)
- [Getting Started](#getting-started)
- [Sanitization](#sanitization)
- [The safe attribute](#the-safe-attribute)
- [The Safe Attribute](#the-safe-attribute)
- [Editor Intellisense and CLI tool](#editor-intellisense-and-cli-tool)
- [Async Components](#async-components)
- [Suspense component](#suspense-component)
- [Error boundaries](#error-boundaries)
- [Why JSX.Element is a Promise?](#why-jsxelement-is-a-promise)
- [Migrating from HTML](#migrating-from-html)
- [Htmx](#htmx)
- [Hotwire Turbo](#hotwire-turbo)
- [Base HTML templates](#base-html-templates)
- [Htmx](#htmx)
- [Compiling HTML](#compiling-html)

@@ -51,3 +60,2 @@ - [Clean Components](#clean-components)

- [The `tag` tag](#the-tag-tag)
- [Async Components](#async-components)
- [Extending types](#extending-types)

@@ -61,99 +69,123 @@ - [Allow everything!](#allow-everything)

<br />
<br />
## Installing
## Preview
```sh
npm install @kitajs/html # or yarn add @kitajs/html
```
<img align="center" src="assets/preview.png" alt="Example of an error thrown by this LSP plugin." />
<br />
## Getting Started
## Installing
Install `@kitajs/html` with your favorite package manager, import it into the top of your `jsx`/`tsx` file and change your tsconfig.json to transpile jsx syntax.
To use the `@kitajs/html` package, follow these steps:
```js
// tsconfig.json
1. Install the required npm packages, `@kitajs/html` and `@kitajs/ts-html-plugin`, to
enable editor intellisense. Open your terminal and run:
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "Html.createElement",
"jsxFragmentFactory": "Html.Fragment"
}
}
```
```sh
npm install @kitajs/html @kitajs/ts-html-plugin
```
```jsx
// Unique import to the top of your main.ts file.
import '@kitajs/html/register'
// Or import it directly everywhere you need it.
import Html from '@kitajs/html'
2. Configure your TypeScript project to transpile TSX/JSX into JavaScript and using our
[LSP Plugin](#editor-intellisense-and-cli-tool). Update your `tsconfig.json` file with
the following settings:
// Using as a simple html builder
console.log(<div>Hello World</div>) // '<div>Hello World</div>'
```jsonc
// tsconfig.json
// Maybe your own server-side html frontend
function route(request, response) {
return response
.header('Content-Type', 'text/html')
.send(<div>Hello World</div>)
}
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "Html.createElement",
"jsxFragmentFactory": "Html.Fragment",
"plugins": [{ "name": "@kitajs/ts-html-plugin" }]
}
}
```
// What about generating a static html file?
fs.writeFileSync(
'index.html',
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello World</div>
</body>
</html>
)
3. Append the
[`xss-scan`](https://github.com/kitajs/ts-html-plugin/tree/main#running-as-cli) command
into your test script. This CLI comes from @kitajs/ts-html-plugin, which catches XSS
vulnerabilities that mey be introduced into your codebase. Open your `package.json`
file and add the following script:
// Also as a component library
function Layout({ name, children }: Html.PropsWithChildren<{ name: string }>) {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello {name}</div>
{children}
</body>
</html>
)
}
```jsonc
// package.json
console.log(<Layout name="World">I'm in the body!</Layout>)
{
"scripts": {
"test": "xss-scan"
}
}
```
// Anywhere you want! All JSX becomes a string
typeof (<div>Hello World</div>) === 'string'
```
4. Ensure that your code editor is using the TypeScript version from your project's
`node_modules` instead of the globally installed TypeScript. For Visual Studio Code,
you can configure this in your workspace settings:
This package just provides functions to transpile JSX to a HTML string, you can imagine doing something like this before, but now with type checking and intellisense:
```jsonc
// .vscode/settings.json
```ts
// without @kitajs/html
const html = `<div> Hello World!<div>` ❌
```
{
"typescript.tsdk": "node_modules/typescript/lib"
}
```
```jsx
// with @kitajs/html
const html = <div>Hello World!<div> ✅
// Also results into a string, but with type checks.
```
> [!WARNING]
> Make sure to verify that your setup is correct by writing `console.log(<div>{'<' + '/div>'}</div>);`
> in your editor. If it **PRODUCE ERRORS**, your setup is correct. Refer to the [@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin)
> repository for more details on setting up editor intellisense.
<br />
## Getting Started
After successfully installing and configuring your project, you can start using JSX syntax
to generate HTML. Here are two options for importing the `@kitajs/html` package:
1. **Import it manually**: Import the package in your TypeScript files where you need it,
avoiding global scope pollution.
```tsx
// my-file.tsx
console.log(<div>Html import needs to be in scope!</div>);
```
2. **Use the register to add a global namespace**: Import the `register` to globally
register all necessary functions for convenience.
```tsx
// Import the register to globally register all needed functions
import '@kitajs/html/register';
// another-file.tsx
console.log(<div>It works without importing!</div>);
```
Now you can use JSX to generate HTML throughout your project. Always use the `safe`
attribute or manually call `Html.escapeHtml` to protect against XSS vulnerabilities when
rendering user input.
Ensuring XSS prevention is vital to guarantee your application's security. You can employ
the [`@kitajs/ts-html-plugin`](https://github.com/kitajs/ts-html-plugin) to catch XSS
issues in your code editor and enhance your code's security.
<br />
## Sanitization
This package sanitizes every attribute by default. However, as the result is always a string, we cannot differentiate a html element created by a `<tag>` or from a user input. This forces you to use the provided [`safe`](#the-safe-attribute) or manually call `Html.escapeHtml`.
> [!IMPORTANT]
> Please utilize our `@kitajs/ts-html-plugin` to emit TypeScript errors wherever you are
> exposed to XSS. Refer to [Getting Started](#getting-started) for installation
> instructions.
```jsx
<div>⚠️ This will NOT be escaped. and WILL expose you to XSS</div>
This package sanitizes every attribute by default. However, since the resulting element is
always a string, it's impossible to differentiate an HTML element created by a `<tag>` or
from user input. This necessitates the use of the provided [`safe`](#the-safe-attribute)
or manual invocation of `Html.escapeHtml`.
```tsx
<div>⚠️ This will NOT be escaped and WILL expose you to XSS</div>
<div attr="This WILL be escaped"></div>

@@ -164,5 +196,5 @@ <div safe>This WILL be escaped</div>

Here's an example of how this is **DANGEROUS** to your application:
Here's an example of how this is **DANGEROUS** for your application:
```jsx
```tsx
user = {

@@ -174,8 +206,12 @@ name: 'Bad guy',

<div class="user-card">{user.description}</div>
// Renders this html which will execute malicious code:
<div class="user-card"><script>getStoredPasswordAndSentToBadGuysServer()</script></div>
// Renders this HTML, which will execute malicious code:
<div class="user-card">
<script>getStoredPasswordAndSentToBadGuysServer()</script>
</div>
<div class="user-card" safe>{user.description}</div>
// Renders this safe html, which will NOT execute any malicious code:
<div>&lt;/div&gt;&lt;script&gt;getStoredPasswordAndSentToBadGuysServer()&lt;/script&gt;</div>
// Renders this safe HTML, which will NOT execute any malicious code:
<div class="user-card">
&lt;/div&gt;&lt;script&gt;getStoredPasswordAndSentToBadGuysServer()&lt;/script&gt;
</div>
```

@@ -185,7 +221,8 @@

### The safe attribute
### The Safe Attribute
You should always use the `safe` attribute when you are rendering uncontrolled user input. This will sanitize its contents and avoid XSS attacks.
Always use the `safe` attribute when rendering uncontrolled user input. This will sanitize
the contents and prevent XSS attacks.
```jsx
```tsx
function UserCard({ name, description, date, about }) {

@@ -198,3 +235,3 @@ return (

<br />
// controlled input, no need to sanitize
// Controlled input, no need to sanitize
<time datetime={date.toISOString()}>{date.toDateString()}</time>

@@ -204,17 +241,246 @@ <br />

</div>
)
);
}
```
Note that only at the very bottom of the HTML tree is where you should use the `safe` attribute, to only escape where its needed.
Note that you should only use the `safe` attribute at the very bottom of the HTML tree
where it's needed.
<br />
## Editor Intellisense and CLI tool
<p align="center">
<img align="center" src="https://github.com/kitajs/ts-html-plugin/blob/main/assets/preview.png?raw=true" alt="Example of an error thrown by @kitajs/ts-html-plugin." width="75%" />
</p>
<h2>⚠️</h2>
**Note:** This section has been relocated to the
[@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin) repository.
Please consult their
"[Getting Started](https://github.com/kitajs/ts-html-plugin#getting-started)" section for
instructions on enabling editor IntelliSense and using the CLI tool.
<br />
<br />
<br />
<br />
## Async Components
Async components are supported. When any child or sub child of a component tree is a
promise, the whole tree will return a promise of the html string.
If no async components are found, the result will be simply a string, and you can safely
cast it into a string.
```tsx
async function Async() {
await callApi();
return <div>Async!</div>;
}
function Sync() {
return <div>Sync!</div>;
}
const async = (
<div>
<Async />
</div>
);
async instanceof Promise;
const sync: string = (
<div>
<Sync />
</div>
);
typeof sync === 'string';
```
A `JSX.Element` will always be a string. Once a children element is a async component, the
entire upper tree will also be async.
[Learn when JSX.Element is a Promise](#why-jsxelement-is-a-promise).
<br />
### Suspense component
The only problem when rendering templates is that you must wait the whole template to be
rendered before sending it to the client. This is not a problem for small templates, but
it can be a problem for large templates.
To solve this problem, we provide a `Suspense` component that combined with
`renderToStream()` rendering method, will stream a fallback component while it waits for
his children to be rendered. This is a perfect combo to use with
[async components](#async-components).
```tsx
import { Suspense, renderToStream } from '@kitajs/html/suspense';
async function MyAsyncComponent() {
const data = await database.query();
return <User name={data.username} />;
}
function renderUserPage(rid: number) {
return (
<Suspense
rid={rid}
fallback={<div>Loading username...</div>}
catch={(err) => <div>Error: {err.stack}</div>}
>
<MyAsyncComponent />
</Suspense>
);
}
// Html is a string readable stream that can be piped to the client
const html = renderToStream(renderUserPage);
```
> [!NOTE]
> There's an open issue to integrate this within a typescript plugin to emit warnings and alerts to use the safe attribute everywhere a variable is used. Wanna help? Check [this issue](https://github.com/kitajs/html/issues/2).
> The `renderToStream()` is returns a native node/bun stream, head over to our
> [suspense-server](examples/suspense-server.tsx) example to see how to use it with
> node:http, Express or Fastify servers.
The above example would render `<div>Loading username...</div>` while waiting for the
`MyAsyncComponent` to be rendered.
When using `Suspense`, you cannot just call the component and get the html string, you
need to use the `renderToStream` function to get a stream that can be piped to the client
with updates. Otherwise, the fallback would render forever.
As the result of any JSX component is always a string, you must use the `rid` provided by
`renderToStream` into all your suspense components, this way we can identify which
suspense is for which request and be able to render concurrent requests.
Suspense also accepts async fallbacks, but it blocks rendering until the fallback is
resolved.
```tsx
function renderTemplate(rid: number) {
return (
<Suspense
rid={rid}
fallback={<MyAsyncFallback />}
catch={(err) => <div>Error: {err.stack}</div>}
>
<MyAsyncComponent />
</Suspense>
);
}
```
The above example would only return anything after `MyAsyncFallback` is resolved. To catch
async fallback errors, you must wrap it into a [`ErrorBoundary`](#error-boundaries).
<br />
### Error boundaries
The same way as promises must be awaited to resolve its own html, errors must be caught.
Outside of suspense components, you can use the provided error boundaries to catch errors.
```tsx
import { ErrorBoundary } from '@kitajs/html/error-boundary';
async function MyAsyncComponent() {
const data = await database.query(); // this promise may reject
return <User name={data.username} />;
}
function renderTemplate() {
return (
<ErrorBoundary catch={(err) => <div>Error: {err.stack}</div>}>
<MyAsyncComponent />
</ErrorBoundary>
);
}
// If MyAsyncComponent throws an error, it will render <div>Error: ...</div>
const html = await renderTemplate();
```
Error boundaries will only work for errors thrown inside async components, for sync
components you must use try/catch.
```tsx
function MySyncComponent() {
try {
const data = database.query(); // this may throw an error
return <User name={data.username} />;
} catch (err) {
return <div>Error: {err.stack}</div>;
}
}
```
Error boundaries outside suspense components will only catch errors thrown by the fallback
component. You must use the Suspense's `catch` property to handle errors thrown by its
children components.
```tsx
function renderTemplate(rid: number) {
return (
<ErrorBoundary catch={<div>fallback error</div>}>
<Suspense
rid={rid}
fallback={<MyAsyncFallback />}
catch={<div>Children error</div>}
>
<MyAsyncComponent />
</Suspense>
</ErrorBoundary>
);
}
const html = renderToStream(renderTemplate);
```
The above example would render `<div>Children error</div>` if `MyAsyncComponent` throws an
error, or `<div>fallback error</div>` if `MyAsyncFallback` throws an error. If both throws
an error, the first error will be changed to the second error as soon as the children
error is thrown.
<br />
### Why JSX.Element is a Promise?
> ℹ️ Until [#14729](https://github.com/microsoft/TypeScript/issues/14729) gets
> implemented, you need to manually cast `JSX.Element` into strings if you are sure there
> is no inner async components in your component tree.
JSX elements are mostly strings everywhere. However, as the nature of this package, once a
children element is a async component, the entire upper tree will also be async. Unless
you are sure that no other component in your entire codebase is async, you should always
handle both string and promise cases.
```tsx
// It may or may not have inner async components.
const html = <Layout />;
if (html instanceof Promise) {
// I'm a promise, I should be awaited
console.log(await html);
} else {
// I'm a string, I can be used as is
console.log(html);
}
```
<br />
## Migrating from HTML
Migrating from plain HTML to JSX can be a pain to convert it all manually, as you will find yourself hand placing quotes and closing void elements. Luckily for us, there's a tool called [htmltojsx](https://magic.reactjs.net/htmltojsx.htm) that can help us with that.
Migrating from plain HTML to JSX can be a pain to convert it all manually, as you will
find yourself hand placing quotes and closing void elements.
You can use [**Html To Jsx**](https://magic.reactjs.net/htmltojsx.htm).
```html

@@ -229,5 +495,5 @@ <!-- Hello world -->

Generates:
Results into:
```jsx
```tsx
<>

@@ -245,38 +511,20 @@ {/* Hello world */}

### Base HTML templates
### Htmx
Often you will have a "template" html with doctype, things on the head, body and so on... The layout is also a very good component to be compiled. Here is a effective example on how to do it:.
The usage of [htmx.org](https://htmx.org/) is super common with this project, this is why
we also provide type definitions for all HTMX attributes.
You just need to add this triple slash directive to the top of your file:
```tsx
export const Layout = html.compile(
(p: Html.PropsWithChildren<{ head: string }>) => (
<>
{'<!doctype html>'}
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Document</title>
{p.head}
</head>
<body>{p.children}</body>
</html>
</>
)
)
/// <reference types="@kitajs/html/htmx.d.ts" />
import '@kitajs/html/register';
const html = (
<Layout
head={
<>
<link rel="stylesheet" href="/style.css" />
<script src="/script.js" />
</>
}>
<div>Hello World</div>
</Layout>
)
// Type checking and intellisense for all HTMX attributes
<div hx-get="/api" hx-trigger="click" hx-target="#target">
Click me!
</div>
);
```

@@ -286,5 +534,7 @@

### Htmx
### Hotwire Turbo
The usage of [htmx.org](https://htmx.org/) is super common with this project, this is why we also provide type definitions for all HTMX attributes.
This project supports the usage of [Turbo Hotwire](https://turbo.hotwired.dev/). We
provide a separate export that you can use to provide type definitions for the elements
and attributes used with Turbo Hotwire.

@@ -294,12 +544,14 @@ You just need to add this triple slash directive to the top of your file:

```tsx
/// <reference types="@kitajs/html/htmx.d.ts" />
/// <reference types="@kitajs/html/hotwire-turbo.d.ts" />
import '@kitajs/html/register'
import '@kitajs/html/register';
const html = (
// Type checking and intellisense for all HTMX attributes
<div hx-get="/api" hx-trigger="click" hx-target="#target">
Click me!
</div>
)
<turbo-frame id="messages">
<a href="/messages/expanded">Show all expanded messages in this frame.</a>
<form action="/messages">Show response from this form within this frame.</form>
</turbo-frame>
);
```

@@ -309,28 +561,69 @@

### Base HTML templates
Often you will have a "template" html with doctype, things on the head, body and so on...
The layout is also a very good component to be compiled. Here is a effective example on
how to do it:.
```tsx
export const Layout = html.compile((p: Html.PropsWithChildren<{ head: string }>) => (
<>
{'<!doctype html>'}
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
{p.head}
</head>
<body>{p.children}</body>
</html>
</>
));
const html = (
<Layout
head={
<>
<link rel="stylesheet" href="/style.css" />
<script src="/script.js" />
</>
}
>
<div>Hello World</div>
</Layout>
);
```
<br />
## Compiling HTML
Compiles a **clean component** into a super fast component. This does not
support unclean components / props processing.
Compiles a **clean component** into a super fast component. This does not support unclean
components / props processing.
This mode works just like prepared statements in SQL. Compiled components can give up to [**2000**](#performance) times faster html generation. This is a opt-in feature that you may not be able to use everywhere!
This mode works just like prepared statements in SQL. Compiled components can give up to
[**2000**](#performance) times faster html generation. This is a opt-in feature that you
may not be able to use everywhere!
```tsx
import Html from '@kitajs/html'
import Html from '@kitajs/html';
function Component(props: Html.PropsWithChildren<{ name: string }>) {
return <div>Hello {props.name}</div>
return <div>Hello {props.name}</div>;
}
const compiled = Html.compile<typeof Component>(Component)
const compiled = Html.compile<typeof Component>(Component);
compiled({ name: 'World' })
compiled({ name: 'World' });
// <div>Hello World</div>
const compiled = Html.compile((p) => <div>Hello {p.name}</div>)
const compiled = Html.compile((p) => <div>Hello {p.name}</div>);
compiled({ name: 'World' })
compiled({ name: 'World' });
// <div>Hello World</div>
```
Properties passed for compiled components **ARE NOT** what will be passed as argument to the generated function.
Properties passed for compiled components **ARE NOT** what will be passed as argument to
the generated function.

@@ -340,10 +633,11 @@ ```tsx

// THIS WILL NOT print 123, but a string used by .compile instead
console.log(t.asd)
return <div></div>
})
console.log(t.asd);
return <div></div>;
});
compiled({ asd: 123 })
compiled({ asd: 123 });
```
That's the reason on why you cannot compile unclean components, as they need to process the props before rendering.
That's the reason on why you cannot compile unclean components, as they need to process
the props before rendering.

@@ -354,6 +648,5 @@ <br />

A **clean component** is a component that does not process props before
applying them to the element. This means that the props are applied to the
element as is, and you need to process them before passing them to the
component.
A **clean component** is a component that does not process props before applying them to
the element. This means that the props are applied to the element as is, and you need to
process them before passing them to the component.

@@ -363,15 +656,15 @@ ```tsx

function Clean(props: PropsWithChildren<{ repeated: string }>) {
return <div>{props.repeated}</div>
return <div>{props.repeated}</div>;
}
// Calculation is done before passing to the component
html = <Clean name={'a'.repeat(5)} />
html = <Clean name={'a'.repeat(5)} />;
// Unclean component, process before render
function Unclean(props: { repeat: string; n: number }) {
return <div>{props.repeat.repeat(props.n)}</div>
return <div>{props.repeat.repeat(props.n)}</div>;
}
// Calculation is done inside the component, thus cannot be used with .compile()
html = <Unclean repeat="a" n={5} />
html = <Unclean repeat="a" n={5} />;
```

@@ -383,5 +676,6 @@

JSX does not allow multiple root elements, but you can use a fragment to group multiple elements:
JSX does not allow multiple root elements, but you can use a fragment to group multiple
elements:
```jsx
```tsx
const html = (

@@ -392,3 +686,3 @@ <>

</>
)
);
```

@@ -407,3 +701,4 @@

**Missing an element or attribute?** Please create an issue or a PR to add it. It's easy to add.
**Missing an element or attribute?** Please create an issue or a PR to add it. It's easy
to add.

@@ -414,3 +709,4 @@ <br />

The `<tag of="">` tag is a custom internal tag that allows you to render any runtime selected tag you want. Possibly reasons to prefer this tag over extending types:
The `<tag of="">` tag is a custom internal tag that allows you to render any runtime
selected tag you want. Possibly reasons to prefer this tag over extending types:

@@ -423,3 +719,3 @@ - You want to render a tag that is chosen at runtime.

```jsx
```tsx
<tag of="asd" />

@@ -432,28 +728,11 @@ // <asd></asd>

We do recommend using [extending types](#extending-types) instead, as it will give you intellisense and type checking.
We do recommend using [extending types](#extending-types) instead, as it will give you
intellisense and type checking.
<br />
## Async Components
Sadly, we cannot allow async components in JSX and keep the same string type for everything else. Even though it should be possible to write async components you will have no real benefit from it, as you will always have to await the whole html generation
to complete before you can render it.
You should fetch async data in the following way:
```jsx
// Fetches all async code beforehand and passes its contents to the component.
async function render(name) {
const data = await api.data(name)
const otherData = await api.otherData(name)
return <Layout data={data} otherData={data} />
}
```
<br />
## Extending types
Just as exemplified above, you may also want to add custom properties to your elements. You can do this by extending the `JSX` namespace.
Just as exemplified above, you may also want to add custom properties to your elements.
You can do this by extending the `JSX` namespace.

@@ -467,6 +746,6 @@ ```tsx

// Changes properties to the math-power element
['my-exponential']: number
['my-exponential']: number;
// this property becomes the <>{children}</> type
children: number
}
children: number;
};
}

@@ -476,3 +755,3 @@

interface HtmlTag {
['hx-boost']: boolean
['hx-boost']: boolean;
// TIP: We already provide HTMX types, check them out!

@@ -487,3 +766,3 @@ }

</mathPower>
)
);
// Becomes <math-power my-exponential="2" hx-boost>3</math-power>

@@ -494,3 +773,4 @@ ```

We also provide a way to allow any tag/attribute combination, altough we **do not recommend using it**.
We also provide a way to allow any tag/attribute combination, altough we **do not
recommend using it**.

@@ -507,5 +787,8 @@ Just add this triple slash directive to the top of your file:

This package is just a string builder on steroids, as you can see [how this works](#how-it-works). This means that most way to isolate performance differences is to micro benchmark.
This package is just a string builder on steroids, as you can see
[how this works](#how-it-works). This means that most way to isolate performance
differences is to micro benchmark.
You can run this yourself by running `pnpm bench`. The bench below was with a Apple M1 Pro 8gb.
You can run this yourself by running `pnpm bench`. The benchmark below ran with a 13600k
@5.1GHz machine.

@@ -515,5 +798,5 @@ ```markdown

- 2023-09-14T20:06:09.655Z
- Node: v18.15.0
- V8: 10.2.154.26-node.25
- 2023-09-23T19:26:24.506Z
- Node: v20.7.0
- V8: 11.3.244.8-node.15
- OS: linux

@@ -526,29 +809,29 @@ - Arch: x64

| ------ | ------------ | ---------- | ----- | ---------- | ---------------- | -------------- |
| 10 | 0.0088ms | 0.0177ms | 2.02x | 0.0066ms | 1.33x | 2.68x |
| 10000 | 0.9501ms | 3.2714ms | 3.44x | 0.6968ms | 1.36x | 4.7x |
| 100000 | 12.2114ms | 19.6453ms | 1.61x | 2.7776ms | 4.4x | 7.07x |
| 10 | 0.0071ms | 0.0123ms | 1.72x | 0.0459ms | 0.16x | 0.27x |
| 10000 | 0.4891ms | 1.9689ms | 4.03x | 0.4315ms | 1.13x | 4.56x |
| 100000 | 4.717ms | 13.653ms | 2.89x | 1.5551ms | 3.03x | 8.78x |
## Many Props
## Mdn Homepage
| Runs | @kitajs/html | typed-html | + | .compile() | + / @kitajs/html | + / typed-html |
| ------ | ------------ | ------------ | ----- | ---------- | ---------------- | -------------- |
| 10 | 0.2479ms | 1.3757ms | 5.55x | 0.0031ms | 79.8x | 442.91x |
| 10000 | 240.6256ms | 982.6665ms | 4.08x | 0.7723ms | 311.56x | 1272.37x |
| 100000 | 2436.4599ms | 10844.2049ms | 4.45x | 4.4179ms | 551.49x | 2454.59x |
| 10 | 0.6441ms | 3.0466ms | 4.73x | 0.0029ms | 221.57x | 1048.03x |
| 10000 | 601.5763ms | 3013.5356ms | 5.01x | 0.2293ms | 2623.21x | 13140.72x |
| 100000 | 5953.055ms | 30076.7341ms | 5.05x | 1.1917ms | 4995.36x | 25238.17x |
## Many Components
## Many Props
| Runs | @kitajs/html | typed-html | + | .compile() | + / @kitajs/html | + / typed-html |
| ------ | ------------ | ----------- | ----- | ---------- | ---------------- | -------------- |
| 10 | 0.2174ms | 0.5805ms | 2.67x | 0.0058ms | 37.43x | 99.91x |
| 10000 | 169.6069ms | 665.6724ms | 3.92x | 1.5865ms | 106.91x | 419.6x |
| 100000 | 1747.2244ms | 5227.3891ms | 2.99x | 7.5666ms | 230.91x | 690.85x |
| 10 | 0.1971ms | 0.9631ms | 4.89x | 0.0026ms | 76.26x | 372.58x |
| 10000 | 164.2186ms | 698.8376ms | 4.26x | 0.682ms | 240.78x | 1024.67x |
| 100000 | 1688.9518ms | 7065.3811ms | 4.18x | 2.8688ms | 588.73x | 2462.82x |
## Big Component
## Many Components
| Runs | @kitajs/html | typed-html | + | .compile() | + / @kitajs/html | + / typed-html |
| ------ | ------------ | ----------- | ----- | ---------- | ---------------- | -------------- |
| 10 | 0.2235ms | 0.8053ms | 3.6x | 0.0048ms | 46.53x | 167.64x |
| 10000 | 175.1073ms | 735.6298ms | 4.2x | 0.9891ms | 177.03x | 743.7x |
| 100000 | 1841.2463ms | 7872.5854ms | 4.28x | 5.1064ms | 360.58x | 1541.72x |
| 10 | 0.2367ms | 0.4481ms | 1.89x | 0.0041ms | 57.64x | 109.14x |
| 10000 | 148.0245ms | 368.8733ms | 2.49x | 1.2694ms | 116.61x | 290.6x |
| 100000 | 1548.758ms | 3748.9902ms | 2.42x | 6.009ms | 257.74x | 623.9x |
```

@@ -560,5 +843,7 @@

This package just aims to be a drop in replacement syntax for JSX, and it works because you [tell tsc to transpile](#getting-started) JSX syntax to calls to our own `html` namespace.
This package just aims to be a drop in replacement syntax for JSX, and it works because
you [tell tsc to transpile](#getting-started) JSX syntax to calls to our own `html`
namespace.
```jsx
```tsx
<ol start={2}>

@@ -578,3 +863,3 @@ {[1, 2].map((i) => (

[1, 2].map((i) => Html.createElement('li', null, i))
)
);
```

@@ -585,3 +870,3 @@

```js
'<ol start="2"><li>1</li><li>2</li></ol>'
'<ol start="2"><li>1</li><li>2</li></ol>';
```

@@ -593,8 +878,9 @@

This package emits HTML as a compact string, useful for over the wire environments. However, if your use case really needs the output
HTML to be pretty printed, you can use an external JS library to do so, like [html-prettify](https://www.npmjs.com/package/html-prettify).
This package emits HTML as a compact string, useful for over the wire environments.
However, if your use case really needs the output HTML to be pretty printed, you can use
an external JS library to do so, like
[html-prettify](https://www.npmjs.com/package/html-prettify).
```jsx
import Html from '@kitajs/html'
import prettify from 'html-prettify'
```tsx
import prettify from 'html-prettify';

@@ -606,8 +892,8 @@ const html = (

</div>
)
);
console.log(html)
console.log(html);
// <div><div>1</div><div>2</div></div>
console.log(prettify(html))
console.log(prettify(html));
// <div>

@@ -619,3 +905,4 @@ // <div>1</div>

👉 There's an open PR to implement this feature natively, wanna work on it? Check [this PR](https://github.com/kitajs/html/pull/1).
👉 There's an open PR to implement this feature natively, wanna work on it? Check
[this PR](https://github.com/kitajs/html/pull/1).

@@ -626,5 +913,9 @@ <br />

This repository was initially forked from [typed-html](https://github.com/nicojs/typed-html) and modified to add some features and increase performance.
This repository was initially forked from
[typed-html](https://github.com/nicojs/typed-html) and modified to add some features and
increase performance.
Initial credits to [nicojs](https://github.com/nicojs) and [contributors](https://github.com/nicojs/typed-html/graphs/contributors) for the amazing work.
Initial credits to [nicojs](https://github.com/nicojs) and
[contributors](https://github.com/nicojs/typed-html/graphs/contributors) for the amazing
work.

@@ -631,0 +922,0 @@ Licensed under the [Apache License, Version 2.0](LICENSE).

declare global {
/**
* The html factory namespace.
*/
var Html: typeof import('./index')
/** The html factory namespace. */
var Html: typeof import('./index');
}
export {}
export {};

@@ -1,17 +0,20 @@

'use strict'
'use strict';
// Finds the global object (window in browsers)
let root
let root;
try {
// eslint-disable-next-line no-new-func
root = Function('return this')()
root = Function('return this')();
/* c8 ignore next 3 */
} catch (_) {
root = window
root = window;
}
root.Html = require('./index')
// Avoids multiple registrations
if (!root.Html) {
root.Html = require('./index');
}
// Removes the default export wrapper
if (root.Html.default) {
root.Html = root.Html.default
root.Html = root.Html.default;
}
## Security
Due to the nature of this project, security is a top priority. If you believe you have
found security issues like XSS attacks or similar, please report them to me ([`security@arthur.place`](mailto:security@arthur.place)) in a
responsible manner. You will receive a response from
me as soon as possible.
found security issues like XSS attacks or similar, please report them to me
([`security@arthur.place`](mailto:security@arthur.place)) in a responsible manner. You
will receive a response from me as soon as possible.
If the issue is confirmed, I will release a patch as soon as possible depending on
complexity but historically within a few days.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc