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 4.1.0 to 4.2.0

6

alpine.js
// No JS code is needed in this file. Its only here to allow direct import of alpine.js
process.emitWarning(
'The `@kitajs/html/alpine` import will be removed in the next major version. See https://github.com/kitajs/html/tree/master/packages/html#deprecating-importing-type-extensions',
'DeprecationWarning',
'KTHTML_DEP'
);

11

error-boundary.d.ts

@@ -7,3 +7,6 @@ import type { Children } from '.';

/** An error thrown by the ErrorBoundary's `timeout` property. */
export class HtmlTimeout extends Error {}
export class HtmlTimeout extends Error {
/** Throws the error. */
static reject(): never;
}

@@ -34,2 +37,8 @@ /**

timeout?: number;
/**
* The error class we should throw if the timeout gets triggered. Defaults to
* {@linkcode HtmlTimeout}
*/
error?: { reject(): never };
}

9

error-boundary.js

@@ -18,3 +18,3 @@ const { contentToString } = require('./index');

children,
setTimeout(props.timeout).then(HtmlTimeout.reject)
setTimeout(props.timeout).then((props.error || HtmlTimeout).reject)
]);

@@ -34,4 +34,5 @@ }

/** @type {import('./error-boundary').HtmlTimeout} */
class HtmlTimeout extends Error {
/** @returns {string} */
/** @returns {never} */
static reject() {

@@ -42,3 +43,3 @@ throw new HtmlTimeout('Children timed out.');

module.exports.ErrorBoundary = ErrorBoundary;
module.exports.HtmlTimeout = HtmlTimeout;
exports.ErrorBoundary = ErrorBoundary;
exports.HtmlTimeout = HtmlTimeout;
// No JS code is needed in this file. Its only here to allow direct import of hotwire-turbo.js
process.emitWarning(
'The `@kitajs/html/hotwire-turbo` import will be removed in the next major version. See https://github.com/kitajs/html/tree/master/packages/html#deprecating-importing-type-extensions',
'DeprecationWarning',
'KTHTML_DEP'
);
// No JS code is needed in this file. Its only here to allow direct import of htmx.js
process.emitWarning(
'The `@kitajs/html/htmx` import will be removed in the next major version. See https://github.com/kitajs/html/tree/master/packages/html#deprecating-importing-type-extensions',
'DeprecationWarning',
'KTHTML_DEP'
);

@@ -121,41 +121,2 @@ /// <reference path="./jsx.d.ts" />

/**
* 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.
*
* @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)} />
*
* // 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} />
* ```
*
* @param htmlComponent The _clean_ component to compile.
* @param strict If it should throw an error when a property is not found. Default is
* `true`
* @param separator The string used to interpolate and separate parameters
* @returns The compiled template function
*/
export function compile<
P extends { [K in keyof P]: K extends 'children' ? Children : string }
>(
this: void,
cleanComponent: Component<P>,
strict?: boolean,
separator?: string
): Component<P>;
/** Here for interop with `preact` and many build systems. */

@@ -215,11 +176,1 @@ export const h: typeof createElement;

export const Html: Omit<typeof import('.'), 'Html'>;
/**
* 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
*/
export as namespace Html;

@@ -389,6 +389,9 @@ /// <reference path="./jsx.d.ts" />

case 'number':
case 'boolean':
// Bigint is the only case where it differs from React.
// where React renders a empty string and we render the whole number.
case 'bigint':
result += content;
continue;
case 'boolean':
continue;
}

@@ -406,7 +409,11 @@

// @ts-ignore - Type instantiation is excessively deep and possibly infinite.
return Promise.all(contents.slice(index)).then(function resolveContents(resolved) {
resolved.unshift(result);
return contentsToString(resolved, escape);
});
if (typeof content.then === 'function') {
// @ts-ignore - Type instantiation is excessively deep and possibly infinite.
return Promise.all(contents.slice(index)).then(function resolveContents(resolved) {
resolved.unshift(result);
return contentsToString(resolved, escape);
});
}
throw new Error('Objects are not valid as a KitaJSX child');
}

@@ -432,7 +439,9 @@

return safe ? escapeHtml(content) : content;
case 'number':
case 'boolean':
// Bigint is the only case where it differs from React.
// where React renders a empty string and we render the whole number.
case 'bigint':
return content.toString();
case 'boolean':
return '';
}

@@ -448,5 +457,9 @@

return content.then(function resolveContent(resolved) {
return contentToString(resolved, safe);
});
if (typeof content.then === 'function') {
return content.then(function resolveContent(resolved) {
return contentToString(resolved, safe);
});
}
throw new Error('Objects are not valid as a KitaJSX child');
}

@@ -469,7 +482,6 @@

if (!hasAttrs) {
attrs = { children: children.length > 1 ? children : children[0] };
} else if (attrs.children === undefined) {
attrs.children = children.length > 1 ? children : children[0];
return name({ children: children.length > 1 ? children : children[0] });
}
attrs.children = children.length > 1 ? children : children[0];
return name(attrs);

@@ -480,3 +492,3 @@ }

if (hasAttrs && name === 'tag') {
name = String(attrs.of);
name = /** @type {string} */ (attrs.of);
}

@@ -494,9 +506,9 @@

if (contents instanceof Promise) {
return contents.then(function resolveContents(child) {
return '<' + name + attributes + '>' + child + '</' + name + '>';
});
if (typeof contents === 'string') {
return '<' + name + attributes + '>' + contents + '</' + name + '>';
}
return '<' + name + attributes + '>' + contents + '</' + name + '>';
return contents.then(function resolveContents(contents) {
return '<' + name + attributes + '>' + contents + '</' + name + '>';
});
}

@@ -506,125 +518,17 @@

function Fragment(props) {
return Html.contentsToString([props.children]);
return contentsToString([props.children]);
}
/**
* Just to stop TS from complaining about the type.
*
* @type {import('.').compile}
* @returns {Function}
*/
function compile(htmlFn, strict = true, separator = '/*\x00*/') {
if (typeof htmlFn !== 'function') {
throw new Error('The first argument must be a function.');
}
const properties = new Set();
const html = htmlFn(
// @ts-expect-error - this proxy will meet the props with children requirements.
new Proxy(
{},
{
get(_, name) {
// Adds the property to the set of known properties.
properties.add(name);
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}`;
}
// Uses ` to avoid content being escaped.
return `\`${separator} + (${access} || ${
strict && !isChildren
? `throwPropertyNotFound(${separator}\`${name.toString()}\`${separator})`
: `${separator}\`\`${separator}`
}) + ${separator}\``;
}
}
)
);
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;
// Escapes every ` without separator
for (; index < length; index++) {
// Escapes the backtick character because it will be used to wrap the string
// in a template literal.
if (
html[index] === '`' &&
html.slice(index - sepLength, index) !== separator &&
html.slice(index + 1, index + sepLength + 1) !== separator
) {
body += html.slice(nextStart, index) + '\\`';
nextStart = index + 1;
}
}
// Adds the remaining string
body += html.slice(nextStart);
if (strict) {
return Function(
'args',
// Checks for args presence
'if (args === undefined) { throw new Error("The arguments object was not provided.") };\n' +
// Function to throw when a property is not found
'function throwPropertyNotFound(name) { throw new Error("Property " + name + " was not provided.") };\n' +
// Concatenates the body
`return \`${body}\``
);
}
return Function(
'args',
// Adds a empty args object when it is not present
'if (args === undefined) { args = Object.create(null) };\n' + `return \`${body}\``
);
}
const Html = {
escape,
e: escape,
escapeHtml,
isVoidElement,
attributesToString,
toKebabCase,
isUpper,
styleToString,
createElement,
h: createElement,
contentsToString,
contentToString,
compile,
Fragment
};
/**
* 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 Html from '@kitajs/html'`
* - `import { Html, type ComponentWithChildren } from '@kitajs/html'`
* - `import Html from '@kitajs/html'`
* - `import Html, { type ComponentWithChildren } from '@kitajs/html'`
*/
module.exports = Html;
module.exports.Html = Html;
module.exports.default = Html;
exports.escape = escape;
exports.e = escape;
exports.escapeHtml = escapeHtml;
exports.isVoidElement = isVoidElement;
exports.attributesToString = attributesToString;
exports.toKebabCase = toKebabCase;
exports.isUpper = isUpper;
exports.styleToString = styleToString;
exports.createElement = createElement;
exports.h = createElement;
exports.contentsToString = contentsToString;
exports.contentToString = contentToString;
exports.Fragment = Fragment;
/// <reference path="./jsx.d.ts" />
/// <reference types="./suspense.d.ts" />
/// <reference types="./error-boundary.d.ts" />
const { Fragment, jsx, jsxs } = require('./jsx-runtime');
const JsxRuntime = {
jsxDEV: jsx,
jsxs,
Fragment
};
module.exports = JsxRuntime;
module.exports.default = JsxRuntime;
exports.jsx = jsx;
exports.jsxs = jsxs;
exports.jsxDEV = jsx;
exports.Fragment = Fragment;
/// <reference path="./jsx.d.ts" />
/// <reference types="./suspense.d.ts" />
/// <reference types="./error-boundary.d.ts" />

@@ -20,3 +22,3 @@ const {

if (name === 'tag') {
name = String(attrs.of);
name = /** @type {string} */ (attrs.of);
}

@@ -52,3 +54,3 @@

if (name === 'tag') {
name = String(attrs.of);
name = /** @type {string} */ (attrs.of);
}

@@ -75,11 +77,5 @@

const JsxRuntime = {
jsx,
jsxs,
// According to the jsx-runtime spec we must export the fragment element also
Fragment
};
module.exports = JsxRuntime;
module.exports.default = JsxRuntime;
exports.jsx = jsx;
exports.jsxs = jsxs;
// According to the jsx-runtime spec we must export the fragment element also
exports.Fragment = Fragment;

@@ -34,6 +34,18 @@ // This file is a result from many sources, including: RFCs, typescript dom lib, w3schools, and others.

contenteditable?: undefined | string;
inputmode?:
| undefined
| 'none'
| 'text'
| 'decimal'
| 'numeric'
| 'tel'
| 'search'
| 'email'
| 'url'
| AnyString;
dir?: undefined | string;
hidden?: undefined | string | boolean;
id?: undefined | number | string;
role?: undefined | string;
popover?: undefined | boolean | 'auto' | 'manual';
role?: undefined | AriaRole;
lang?: undefined | string;

@@ -283,5 +295,5 @@ draggable?: undefined | string | boolean;

height?: undefined | number | string;
decoding?: 'sync' | 'async' | 'auto' | AnyString;
loading?: 'eager' | 'lazy' | AnyString;
srcset?: string;
decoding?: undefined | 'sync' | 'async' | 'auto' | AnyString;
loading?: undefined | 'eager' | 'lazy' | AnyString;
srcset?: undefined | string;
}

@@ -464,3 +476,3 @@

interface HtmlSelectTag extends HtmlTag {
interface HtmlSelectTag extends HtmlTag, FormEvents {
autofocus?: undefined | boolean;

@@ -473,2 +485,3 @@ disabled?: undefined | boolean;

size?: undefined | string;
autocomplete?: undefined | string;
}

@@ -660,3 +673,3 @@

*/
key?: never;
key?: undefined | never;
}

@@ -847,1 +860,74 @@

}
// All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
type AriaRole =
| 'alert'
| 'alertdialog'
| 'application'
| 'article'
| 'banner'
| 'button'
| 'cell'
| 'checkbox'
| 'columnheader'
| 'combobox'
| 'complementary'
| 'contentinfo'
| 'definition'
| 'dialog'
| 'directory'
| 'document'
| 'feed'
| 'figure'
| 'form'
| 'grid'
| 'gridcell'
| 'group'
| 'heading'
| 'img'
| 'link'
| 'list'
| 'listbox'
| 'listitem'
| 'log'
| 'main'
| 'marquee'
| 'math'
| 'menu'
| 'menubar'
| 'menuitem'
| 'menuitemcheckbox'
| 'menuitemradio'
| 'navigation'
| 'none'
| 'note'
| 'option'
| 'presentation'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'region'
| 'row'
| 'rowgroup'
| 'rowheader'
| 'scrollbar'
| 'search'
| 'searchbox'
| 'separator'
| 'slider'
| 'spinbutton'
| 'status'
| 'switch'
| 'tab'
| 'table'
| 'tablist'
| 'tabpanel'
| 'term'
| 'textbox'
| 'timer'
| 'toolbar'
| 'tooltip'
| 'tree'
| 'treegrid'
| 'treeitem'
| (string & {});
// No JS code is needed in this file. Its only here to allow direct import of jsx.js
process.emitWarning(
'The `@kitajs/html/jsx` import will be removed in the next major version. See https://github.com/kitajs/html/tree/master/packages/html#deprecating-importing-type-extensions',
'DeprecationWarning',
'KTHTML_DEP'
);
{
"name": "@kitajs/html",
"version": "4.1.0",
"version": "4.2.0",
"description": "Fast and type safe HTML templates using TypeScript.",

@@ -18,3 +18,15 @@ "homepage": "https://github.com/kitajs/html/tree/master/packages/html#readme",

],
"type": "commonjs",
"exports": {
".": "./index.js",
"./jsx-runtime": "./jsx-runtime.js",
"./jsx-dev-runtime": "./jsx-dev-runtime.js",
"./package.json": "./package.json",
"./suspense": "./suspense.js",
"./register": "./register.js",
"./error-boundary": "./error-boundary.js",
"./*": "./*"
},
"main": "index.js",
"module": "index.js",
"types": "index.d.ts",

@@ -25,8 +37,9 @@ "dependencies": {

"devDependencies": {
"@types/jsdom": "^21.1.6",
"@types/node": "^20.12.2",
"c8": "^9.1.0",
"jsdom": "^24.0.0",
"tslib": "^2.6.2",
"typescript": "^5.4.3"
"@types/jsdom": "^21.1.7",
"@types/node": "^20.14.11",
"c8": "^10.1.2",
"jsdom": "^24.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.5.4"
},

@@ -33,0 +46,0 @@ "engines": {

@@ -61,4 +61,2 @@ <p align="center">

- [Base HTML templates](#base-html-templates)
- [Compiling HTML](#compiling-html)
- [Clean Components](#clean-components)
- [Fragments](#fragments)

@@ -75,2 +73,3 @@ - [Supported HTML](#supported-html)

- [Deprecating global register](#deprecating-global-register)
- [Deprecating importing type extensions](#deprecating-importing-type-extensions)
- [Fork credits](#fork-credits)

@@ -748,89 +747,2 @@

## Compiling HTML
`Html.compile` interface compiles a [clean component](#clean-components) into a super fast
component. This does not support unclean components / props processing.
<br />
> [!WARNING]
> This feature is a special use case for rendering **entire page templates** like what you
> would do with handlebars or nunjucks.
>
> It does not works with mostly JSX components and, for small components,
> [it will be slower than the normal](benchmark.md) JSX syntax.
<br />
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!
Due to the nature of
[`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
objects, the spread operator (`...`) will not work with compiled components. You need to
manually pass all props to their components.
```tsx
import Html from '@kitajs/html';
function Component(props: Html.PropsWithChildren<{ name: string }>) {
return <div>Hello {props.name}</div>;
}
const compiled = Html.compile<typeof Component>(Component);
compiled({ name: 'World' });
// <div>Hello World</div>
const compiled = Html.compile((p) => <div>Hello {p.name}</div>);
compiled({ name: 'World' });
// <div>Hello World</div>
```
Properties passed for compiled components **ARE NOT** what will be passed as argument to
the generated function.
```tsx
const compiled = Html.compile((t) => {
// THIS WILL NOT print 123, but a string used by .compile instead
console.log(t.asd);
return <div></div>;
});
compiled({ asd: 123 });
```
That's the reason on why you cannot compile unclean components, as they need to process
the props before rendering.
<br />
### Clean Components
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.
```tsx
// Clean component, render as is
function Clean(props: PropsWithChildren<{ sum: number }>) {
return <div>{props.sum}</div>;
}
// Calculation is done before passing to the component
html = <Clean sum={3 * 2} />;
// Unclean component, process before render
function Unclean(props: { a: number; b: number }) {
return <div>{props.a * props.b}</div>;
}
// Calculation is done inside the component, thus cannot be used with .compile()
html = <Unclean a={3} b={2} />;
```
<br />
## Fragments

@@ -1114,4 +1026,4 @@

The `@kitajs/html/register` import has been deprecated and will be removed in the next
major version. Please change the way you have configured your project to use this library.
The `@kitajs/html/register` in favour of the `react-jsx` target `@kitajs/html` supports,
which automatically registers the JSX runtime globally.

@@ -1143,2 +1055,29 @@ Please update your tsconfig to use the new `jsxImportSource` option and remove all

## Deprecating importing type extensions
Importing type extensions like `import '@kitajs/html/htmx'` and
`import '@kitajs/html/alpine'` have been deprecated and will be removed in the next major
version.
Please change the way you import them to either use `/// <reference types="..." />`
[triple slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html)
or the [`types`](https://www.typescriptlang.org/tsconfig/#types) option in your tsconfig.
```diff
- import '@kitajs/html/htmx';
+ /// <reference types="@kitajs/html/htmx" />
```
**Or** add them in the `types` option present in your tsconfig:
```diff
{
"compilerOptions": {
+ "types": ["@kitajs/html/htmx"]
}
}
```
<br />
## Fork credits

@@ -1145,0 +1084,0 @@

@@ -60,4 +60,4 @@ import type { Readable } from 'node:stream';

* **Warning**: Using `Suspense` without any type of runtime support will _**LEAK
* memory**_. Always use `renderToStream`, `renderToString` or within a specific package
* like `@kitajs/fastify-html-plugin`
* memory**_ and not work. Always use with `renderToStream` or within a framework that
* supports it.
*/

@@ -73,2 +73,4 @@ export function Suspense(props: SuspenseProps): JSX.Element;

* ```tsx
* import { text} from 'node:stream/consumers';
*
* // Prints out the rendered stream (2nd example shows with a custom id)

@@ -78,5 +80,9 @@ * const stream = renderToStream(r => <AppWithSuspense rid={r} />)

*
* // You can consume it as a stream
* for await (const html of stream) {
* console.log(html.toString())
* }
*
* // Or join it all together (Wastes ALL Suspense benefits, but useful for testing)
* console.log(await text(stream))
* ```

@@ -95,32 +101,28 @@ *

/**
* Returns a promise that resolves to the entire HTML generated by the component tree.
* **Suspense calls are waited for**.
* Joins the html base template (with possible suspense's fallbacks) with the request data
* and returns the final Readable to be piped into the response stream.
*
* This method is a shorthand to call {@linkcode renderToStream} and collect its result
* into a string.
* **This API is meant to be used by library authors and should not be used directly.**
*
* **Rendering to string will not give any benefits over streaming, it will only be
* slower.**
*
* @example
*
* ```tsx
* // Does not uses suspense benefits! Useful for testing. Prefer to
* // use renderToStream instead. (2nd example shows with a custom id)
* const html = await renderToString(r => <AppWithSuspense rid={r} />)
* const html = await renderToString(<AppWithSuspense rid={myCustomId} />, myCustomId)
* const html = <RootLayout rid={rid} />
* const requestData = SUSPENSE_ROOT.requests.get(rid);
*
* console.log(html);
* if(!requestData) {
* return html;
* }
*
* // This prepends the html into the stream, handling possible
* // cases where the html resolved after one of its async children
* return writeFallback(html, requestData.stream);
* ```
*
* @param html The component tree to render or a function that returns the component tree.
* @param rid The request id to identify the request, if not provided, a new incrementing
* id will be used.
* @returns A promise that resolves to the entire HTML generated by the component tree.
* @param fallback The fallback to render while the async children are loading.
* @param stream The stream to write the fallback into.
* @returns The same stream or another one with the fallback prepended.
* @see {@linkcode renderToStream}
*/
export function renderToString(
html: JSX.Element | ((rid: number | string) => JSX.Element),
rid?: number | string
): Promise<string>;
export function resolveHtmlStream(template: JSX.Element, data: RequestData): Readable;

@@ -133,3 +135,3 @@ /**

*/
export const SuspenseScript: string;
export declare const SuspenseScript: string;

@@ -136,0 +138,0 @@ /**

const { contentsToString, contentToString } = require('./index');
const { Readable } = require('node:stream');
const { Readable, PassThrough } = require('node:stream');

@@ -116,3 +116,3 @@ // Avoids double initialization in case this file is not cached by

children
void children
.then(writeStreamTemplate)

@@ -144,8 +144,3 @@ .catch(function errorRecover(error) {

.catch(function writeFatalError(error) {
// stream.emit returns true if there's a listener
// Nothing else to do if no catch or listener was found
/* c8 ignore next 3 */
if (data?.stream.emit('error', error) === false) {
console.error(error);
}
data.stream.emit('error', error);
})

@@ -156,12 +151,12 @@ .finally(function clearRequestData() {

data.running -= 1;
return;
}
// Last suspense component, runs cleanup
} else {
if (data && !data.stream.closed) {
data.stream.push(null);
}
// Last suspense component, runs cleanup
if (data && !data.stream.closed) {
data.stream.push(null);
}
// Removes the current state
SUSPENSE_ROOT.requests.delete(props.rid);
}
// Removes the current state
SUSPENSE_ROOT.requests.delete(props.rid);
});

@@ -175,7 +170,7 @@

if (typeof fallback === 'string') {
return `<div id="B:${run}" data-sf>${fallback}</div>`;
return '<div id="B:' + run + '" data-sf>' + fallback + '</div>';
}
return fallback.then(function resolveCallback(resolved) {
return `<div id="B:${run}" data-sf>${resolved}</div>`;
return '<div id="B:' + run + '" data-sf>' + resolved + '</div>';
});

@@ -221,4 +216,13 @@

} else if (SUSPENSE_ROOT.requests.has(rid)) {
// Ensures the request id is unique
throw new Error(`The provided Request Id is already in use: ${rid}.`);
// Ensures the request id is unique within the current request
// error here to keep original stack trace
const error = new Error(`The provided Request Id is already in use: ${rid}.`);
// returns errored stream to avoid throws
return new Readable({
read() {
this.emit('error', error);
this.push(null);
}
});
}

@@ -232,3 +236,10 @@

SUSPENSE_ROOT.requests.delete(rid);
throw error;
// returns errored stream to avoid throws
return new Readable({
read() {
this.emit('error', error);
this.push(null);
}
});
}

@@ -246,55 +257,46 @@ }

const readable = new Readable({ read: noop });
html.then(
(result) => {
readable.push(result);
readable.push(null); // self closes
},
(error) => {
// stream.emit returns true if there's a listener
// Nothing else to do if no catch or listener was found
/* c8 ignore next 3 */
if (readable.emit('error', error) === false) {
console.error(error);
}
return new Readable({
read() {
void html
.then((result) => {
this.push(result);
this.push(null);
})
.catch((error) => {
this.emit('error', error);
});
}
);
return readable;
});
}
if (typeof html === 'string') {
requestData.stream.push(html);
} else {
html.then(
(html) => requestData.stream.push(html),
(error) => {
/* c8 ignore next 6 */
// stream.emit returns true if there's a listener
// Nothing else to do if no catch or listener was found
if (requestData.stream.emit('error', error) === false) {
console.error(error);
}
}
);
return resolveHtmlStream(html, requestData);
}
/** @type {import('./suspense').resolveHtmlStream} */
function resolveHtmlStream(template, requestData) {
// Impossible to sync templates have their
// streams being written (sent = true) before the fallback
if (typeof template === 'string') {
requestData.stream.push(template);
return requestData.stream;
}
return requestData.stream;
}
const prepended = new PassThrough();
/** @type {import('./suspense').renderToString} */
async function renderToString(factory, rid) {
const chunks = [];
void template.then(
(result) => {
prepended.push(result);
requestData.stream.pipe(prepended);
},
(error) => {
prepended.emit('error', error);
}
);
for await (const chunk of renderToStream(factory, rid)) {
chunks.push(chunk);
}
return chunks.join('');
return prepended;
}
module.exports.Suspense = Suspense;
module.exports.renderToStream = renderToStream;
module.exports.renderToString = renderToString;
module.exports.SuspenseScript = SuspenseScript;
exports.Suspense = Suspense;
exports.renderToStream = renderToStream;
exports.resolveHtmlStream = resolveHtmlStream;
exports.SuspenseScript = SuspenseScript;

Sorry, the diff of this file is not supported yet

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