diff --git a/.changeset/makes_tsx_happy.md b/.changeset/makes_tsx_happy.md
index 3a7b1a08d..c301daa6a 100644
--- a/.changeset/makes_tsx_happy.md
+++ b/.changeset/makes_tsx_happy.md
@@ -1,5 +1,7 @@
---
-'@kitajs/html': minor
+'@kitajs/html': patch
---
-Makes [tsx](https://www.npmjs.com/package/tsx) and the underling [esbuild](https://esbuild.github.io/) ecosystem happy by exporting the `Fragment`, `jsx` and `jsxs` from `@kitajs/html/jsx-runtime`
+Makes [tsx](https://www.npmjs.com/package/tsx) and the underling
+[esbuild](https://esbuild.github.io/) ecosystem happy by exporting the `Fragment`, `jsx`
+and `jsxs` from `@kitajs/html/jsx-runtime`
diff --git a/.changeset/polite-rivers-run.md b/.changeset/polite-rivers-run.md
new file mode 100644
index 000000000..0016948fa
--- /dev/null
+++ b/.changeset/polite-rivers-run.md
@@ -0,0 +1,5 @@
+---
+'@kitajs/html': patch
+---
+
+Removed Html.compile support
diff --git a/.changeset/ten-pandas-impress.md b/.changeset/ten-pandas-impress.md
new file mode 100644
index 000000000..96acb6c98
--- /dev/null
+++ b/.changeset/ten-pandas-impress.md
@@ -0,0 +1,6 @@
+---
+'@kitajs/fastify-html-plugin': patch
+'@kitajs/html': patch
+---
+
+Added ESM support
diff --git a/README.md b/README.md
index 954982bb9..2003a3999 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
![image](https://github.com/kitajs/html/assets/47537704/edd85ef1-9469-475e-83f8-c6819cf2cc1b)
(Component);
-
-compiled({ name: 'World' });
-// Hello World
-
-const compiled = Html.compile((p) => Hello {p.name}
);
-
-compiled({ name: 'World' });
-// Hello World
-```
-
-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 ;
-});
-
-compiled({ asd: 123 });
-```
-
-That's the reason on why you cannot compile unclean components, as they need to process
-the props before rendering.
-
-
-
-### 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 {props.sum}
;
-}
-
-// Calculation is done before passing to the component
-html = ;
-
-// Unclean component, process before render
-function Unclean(props: { a: number; b: number }) {
- return {props.a * props.b}
;
-}
-
-// Calculation is done inside the component, thus cannot be used with .compile()
-html = ;
-```
-
-
-
## Fragments
JSX does not allow multiple root elements, but you can use a fragment to group multiple
@@ -1108,8 +1020,8 @@ console.log(prettify(html));
## Deprecating global register
-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.
Please update your tsconfig to use the new `jsxImportSource` option and remove all
references to `'@kitajs/html/register'` from your codebase.
@@ -1136,6 +1048,33 @@ codebase.
+## 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 `/// `
+[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';
++ ///
+```
+
+**Or** add them in the `types` option present in your tsconfig:
+
+```diff
+{
+ "compilerOptions": {
++ "types": ["@kitajs/html/htmx"]
+ }
+}
+```
+
+
+
## Fork credits
This repository was initially forked from
diff --git a/packages/html/index.d.ts b/packages/html/index.d.ts
index dec2c94ae..e95a7293a 100644
--- a/packages/html/index.d.ts
+++ b/packages/html/index.d.ts
@@ -118,45 +118,6 @@ export function contentToString(
escape?: boolean
): JSX.Element;
-/**
- * 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 {props.repeated}
}
- *
- * // Calculation is done before passing to the component
- * html =
- *
- * // Unclean component, process before render
- * function Unclean(props: { repeat: string; n: number }) { return {props.repeat.repeat(props.n)}
}
- *
- * // Calculation is done inside the component, thus cannot be used with .compile() html =
- *
- * ```
- *
- * @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,
- strict?: boolean,
- separator?: string
-): Component
;
-
/** Here for interop with `preact` and many build systems. */
export const h: typeof createElement;
@@ -211,13 +172,3 @@ export type Component = (this: void, props: PropsWithChildren) => JSX
* @link https://www.npmjs.com/package/@kitajs/html
*/
export const Html: Omit;
-
-/**
- * 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;
diff --git a/packages/html/index.js b/packages/html/index.js
index 3615cd939..5f8f53bba 100644
--- a/packages/html/index.js
+++ b/packages/html/index.js
@@ -462,17 +462,16 @@ function createElement(name, attrs, ...children) {
// We at least need to pass the children to the function component. We may receive null if this
// component was called without any children.
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);
}
// Switches the tag name when this custom `tag` is present.
if (hasAttrs && name === 'tag') {
- name = String(attrs.of);
+ name = /** @type {string} */ (attrs.of);
}
const attributes = hasAttrs ? attributesToString(attrs) : '';
@@ -485,138 +484,30 @@ function createElement(name, attrs, ...children) {
const contents = contentsToString(children, hasAttrs && attrs.safe);
- 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 + '>';
+ });
}
/** @type {import('.').Fragment} */
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;
diff --git a/packages/html/jsx-dev-runtime.js b/packages/html/jsx-dev-runtime.js
index cc50faf54..014acd53f 100644
--- a/packages/html/jsx-dev-runtime.js
+++ b/packages/html/jsx-dev-runtime.js
@@ -1,12 +1,10 @@
///
+///
+///
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;
diff --git a/packages/html/jsx-runtime.js b/packages/html/jsx-runtime.js
index 475f2db83..73e1a388e 100644
--- a/packages/html/jsx-runtime.js
+++ b/packages/html/jsx-runtime.js
@@ -1,4 +1,6 @@
///
+///
+///
const {
Fragment,
@@ -17,7 +19,7 @@ function jsx(name, attrs) {
// Switches the tag name when this custom `tag` is present.
if (name === 'tag') {
- name = String(attrs.of);
+ name = /** @type {string} */ (attrs.of);
}
const attributes = attributesToString(attrs);
@@ -48,7 +50,7 @@ function jsxs(name, attrs) {
// Switches the tag name when this custom `tag` is present.
if (name === 'tag') {
- name = String(attrs.of);
+ name = /** @type {string} */ (attrs.of);
}
const attributes = attributesToString(attrs);
@@ -70,16 +72,7 @@ function jsxs(name, attrs) {
return '<' + name + attributes + '>' + contents + '' + name + '>';
}
-const JsxRuntime = {
- jsx,
- jsxs,
-
- // According to the jsx-runtime spec we must export the fragment element also
- Fragment
-};
-
-module.exports = JsxRuntime;
exports.jsx = jsx;
exports.jsxs = jsxs;
+// According to the jsx-runtime spec we must export the fragment element also
exports.Fragment = Fragment;
-module.exports.default = JsxRuntime;
diff --git a/packages/html/package.json b/packages/html/package.json
index bd34f3fc1..a653a1793 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -15,7 +15,15 @@
"sideEffects": [
"register.js"
],
+ "exports": {
+ ".": "./index.js",
+ "./jsx-runtime": "./jsx-runtime.js",
+ "./jsx-dev-runtime": "./jsx-runtime.js",
+ "./package.json": "./package.json",
+ "./*": "./*"
+ },
"main": "index.js",
+ "module": "index.js",
"types": "index.d.ts",
"scripts": {
"bench": "pnpm --filter \"benchmark-*\" build && pnpm --filter \"benchmark-runner\" start",
diff --git a/packages/html/suspense.js b/packages/html/suspense.js
index 5d1ab7c3a..f4ea1347a 100644
--- a/packages/html/suspense.js
+++ b/packages/html/suspense.js
@@ -169,11 +169,11 @@ function Suspense(props) {
// Keeps string return type
if (typeof fallback === 'string') {
- return `${fallback}
`;
+ return '' + fallback + '
';
}
return fallback.then(function resolveCallback(resolved) {
- return `${resolved}
`;
+ return '' + resolved + '
';
});
/**
@@ -287,7 +287,7 @@ async function renderToString(factory, rid) {
return chunks.join('');
}
-module.exports.Suspense = Suspense;
-module.exports.renderToStream = renderToStream;
-module.exports.renderToString = renderToString;
-module.exports.SuspenseScript = SuspenseScript;
+exports.Suspense = Suspense;
+exports.renderToStream = renderToStream;
+exports.renderToString = renderToString;
+exports.SuspenseScript = SuspenseScript;