From c0fc4b37dc4415765e9b3f072696fbf8c084234c Mon Sep 17 00:00:00 2001
From: ncpa0cpl <32438334+ncpa0cpl@users.noreply.github.com>
Date: Wed, 3 Jan 2024 09:37:15 +0100
Subject: [PATCH] feat: removed the `renderToStringTemplateTag` render function
---
README.md | 60 +----
src/html-renderer/element-resolver.ts | 2 +-
src/index.ts | 7 -
src/string-template-renderer/default-cache.ts | 36 ---
src/string-template-renderer/interpolate.ts | 93 --------
.../jsx-elem-to-strings.ts | 207 ------------------
.../render-to-string-template-tag.ts | 77 -------
.../resolve-element.ts | 25 ---
.../string-template-tag-type.ts | 4 -
.../to-template-string-array.ts | 16 --
.../map-attribute-name.ts | 2 +-
11 files changed, 4 insertions(+), 525 deletions(-)
delete mode 100644 src/string-template-renderer/default-cache.ts
delete mode 100644 src/string-template-renderer/interpolate.ts
delete mode 100644 src/string-template-renderer/jsx-elem-to-strings.ts
delete mode 100644 src/string-template-renderer/render-to-string-template-tag.ts
delete mode 100644 src/string-template-renderer/resolve-element.ts
delete mode 100644 src/string-template-renderer/string-template-tag-type.ts
delete mode 100644 src/string-template-renderer/to-template-string-array.ts
rename src/{string-template-renderer => utilities}/map-attribute-name.ts (85%)
diff --git a/README.md b/README.md
index f7ddfbd..18d5450 100644
--- a/README.md
+++ b/README.md
@@ -19,10 +19,8 @@ A JSX based html templating engine for browsers or Node environments.
1. [Adding custom web component tags](#adding-custom-web-component-tags)
2. [Adding a global html attribute](#adding-a-global-html-attribute)
8. [Express JS View Engine](#express-js-view-engine)
-9. [Rendering to a string tag template](#rendering-to-a-string-tag-template)
- 1. [Example](#example-2)
-10. [Monkey-Patching type definitions](#monkey-patching-type-definitions)
-11. [Contributing](#contributing)
+9. [Monkey-Patching type definitions](#monkey-patching-type-definitions)
+10. [Contributing](#contributing)
## Getting started
@@ -348,60 +346,6 @@ app.get("/", (_, resp) => {
For this approach to work, the JSX Components must be exported as defaults (ex. `export default () =>
` or `exports.default = () => `) and the views must be transpiled to `.js` files.
-## Rendering to a string tag template
-
-A [string tag template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) is special type of function that can be used for custom parsing of template literals.
-
-JSXTE allows you to leverage any existing string tag templates but with a JSX syntax instead of a template literal.
-
-### Example
-
-```tsx
-// using template literal:
-import { html } from "some-library";
-const props = {
- /* ... */
-};
-const result = html`
-
${props.header}
-
`;
-
-// using JSXTE:
-import { renderToStringTemplateTag } from "jsxte";
-import { html } from "some-library";
-const props = {
- /* ... */
-};
-const result = renderToStringTemplateTag(
- html,
-
-
{props.header}
-
,
-);
-```
-
-If the string tag template function uses non standard html attribute names (ex. `className` instead of `class` or `@click` instead of `onclick`) you can map the attribute names render by this method by specifying mapping for those:
-
-```tsx
-const result = renderToStringTemplateTag(
- html,
-
-
-
,
- {
- attributeMap: {
- onclick: "@click",
- class: "className",
- },
- },
-);
-```
-
## Monkey-Patching type definitions
It is possible to monkey-patch type definition of all HTML tags and add new attributes to them.
diff --git a/src/html-renderer/element-resolver.ts b/src/html-renderer/element-resolver.ts
index 6277382..3b38ce2 100644
--- a/src/html-renderer/element-resolver.ts
+++ b/src/html-renderer/element-resolver.ts
@@ -1,4 +1,4 @@
-import { mapAttributeName } from "../string-template-renderer/map-attribute-name";
+import { mapAttributeName } from "../utilities/map-attribute-name";
import type { HTMLElementStruct, RendererHTMLAttributes } from "./types";
export class HTMLElementResolver {
diff --git a/src/index.ts b/src/index.ts
index 4f008a9..536c2e7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -3,11 +3,6 @@ import "./utilities/array-flat-polyfill";
export * from "./express/index";
export * from "./jsx/jsx.types";
-export {
- Interpolate,
- InterpolateTag,
-} from "./string-template-renderer/interpolate";
-export { DefaultTemplateArrayCache } from "./string-template-renderer/default-cache";
export { ErrorBoundary } from "./error-boundary/error-boundary";
export { defineContext } from "./component-api/component-api";
export {
@@ -18,7 +13,6 @@ export {
renderToJson,
renderToJsonAsync,
} from "./json-renderer/render-to-json";
-export { renderToStringTemplateTag } from "./string-template-renderer/render-to-string-template-tag";
export { memo } from "./utilities/memo";
export { createElement } from "./jsx/jsx-runtime";
export { JsxteRenderError } from "./jsxte-render-error";
@@ -28,7 +22,6 @@ export type {
ComponentApi,
} from "./component-api/component-api";
export type { AttributeBool, HTMLProps } from "./jsx/base-html-tag-props";
-export type { StringTemplateParserOptions } from "./string-template-renderer/render-to-string-template-tag";
export type { Crossorigin } from "./jsx/prop-types/shared/crossorigin";
export type { RefererPolicy } from "./jsx/prop-types/shared/referer-policy";
export type { Target } from "./jsx/prop-types/shared/target";
diff --git a/src/string-template-renderer/default-cache.ts b/src/string-template-renderer/default-cache.ts
deleted file mode 100644
index 421c3cf..0000000
--- a/src/string-template-renderer/default-cache.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { TemplateLiteralCache } from "./render-to-string-template-tag";
-
-const arrIsEqual = (
- a: TemplateStringsArray,
- b: TemplateStringsArray,
-) => {
- if (a.length !== b.length) return false;
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
- return true;
-};
-
-export class DefaultTemplateArrayCache
- implements TemplateLiteralCache
-{
- private entries: Array = [];
-
- set(templateArray: TemplateStringsArray) {
- this.entries.push(templateArray);
- }
-
- get(
- templateArray: TemplateStringsArray,
- ): TemplateStringsArray | undefined {
- for (let i = 0; i < this.entries.length; i++) {
- if (arrIsEqual(this.entries[i]!, templateArray)) {
- return this.entries[i];
- }
- }
-
- return undefined;
- }
-
- clear() {
- this.entries = [];
- }
-}
diff --git a/src/string-template-renderer/interpolate.ts b/src/string-template-renderer/interpolate.ts
deleted file mode 100644
index 95c2b06..0000000
--- a/src/string-template-renderer/interpolate.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Only fo use with the `renderToStringTemplateTag` render function.
- *
- * Whatever is passed to this component as children will be interpolated into
- * the template string.
- *
- * Example
- *
- * ```tsx
- * function someFunction() {}
- *
- * renderToStringTemplateTag(
- * html,
- *
- *
- * {someFunction}
- *
- *
- * );
- *
- * // The above is equivalent to:
- *
- * html`
- *
- * ${someFunction}
- *
- * `;
- * ```
- */
-export class Interpolate {
- /**
- * @internal
- */
- static _isInterpolate(o: any): o is typeof Interpolate {
- const canBeClass = typeof o === "function";
- const isNotNull = o !== null;
-
- if (!canBeClass || !isNotNull) return false;
-
- const baseName = (o as any as typeof Interpolate)._baseName;
- return baseName === this._baseName;
- }
-
- private static _baseName = "Interpolate";
-
- constructor(_: { children?: any }) {}
-}
-
-/**
- * Only fo use with the `renderToStringTemplateTag` render function.
- *
- * Whatever is passed to this component as children will be rendered using the
- * tag function and interpolated into the template string.
- *
- * Example
- *
- * ```tsx
- * renderToStringTemplateTag(
- * html,
- *
- *
- * Hello World
- *
- *
- * );
- *
- * // The above is equivalent to:
- *
- * html`
- *
- * ${html`Hello World`}
- *
- * `;
- * ```
- */
-export class InterpolateTag {
- /**
- * @internal
- */
- static _isInterpolateRender(o: any): o is typeof InterpolateTag {
- const canBeClass = typeof o === "function";
- const isNotNull = o !== null;
-
- if (!canBeClass || !isNotNull) return false;
-
- const baseName = (o as any as typeof InterpolateTag)._baseName;
- return baseName === this._baseName;
- }
-
- private static _baseName = "InterpolateTag";
-
- constructor(_: { children?: any }) {}
-}
diff --git a/src/string-template-renderer/jsx-elem-to-strings.ts b/src/string-template-renderer/jsx-elem-to-strings.ts
deleted file mode 100644
index 8f07168..0000000
--- a/src/string-template-renderer/jsx-elem-to-strings.ts
+++ /dev/null
@@ -1,207 +0,0 @@
-import { ComponentApi } from "../component-api/component-api";
-import { ErrorBoundary } from "../error-boundary/error-boundary";
-import { createElement } from "../jsx-runtime";
-import { SELF_CLOSING_TAG_LIST } from "../utilities/self-closing-tag-list";
-import { Interpolate, InterpolateTag } from "./interpolate";
-import { mapAttributeName } from "./map-attribute-name";
-import type { StringTemplateParserOptions } from "./render-to-string-template-tag";
-import { resolveElement } from "./resolve-element";
-import type { StringTemplateTag } from "./string-template-tag-type";
-import { toTemplateStringArray } from "./to-template-string-array";
-
-export type StringTemplateParserInternalOptions =
- StringTemplateParserOptions & {
- tag: StringTemplateTag;
- };
-
-function assertSyncElem(
- e: JSXTE.TagElement | JSXTE.TextNodeElement | JSX.AsyncElement,
-): asserts e is JSXTE.SyncElement {}
-
-type TagFunctionArgs = [string[], any[]];
-
-const concatToLastStringOrPush = (a: TagFunctionArgs, s?: string) => {
- if (s) {
- if (a[0][a[0].length - 1] !== undefined) {
- a[0][a[0].length - 1] += s;
- } else {
- a[0].push(s);
- }
- }
-};
-
-export const jsxElemToTagFuncArgsSync = (
- element: JSX.Element,
- options: StringTemplateParserInternalOptions,
- _componentApi: ComponentApi = ComponentApi.create(),
-): TagFunctionArgs => {
- // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
- switch (typeof element) {
- case "string":
- return [["", ""], [element]];
- case "bigint":
- case "number":
- return [["", ""], [String(element)]];
- case "boolean":
- case "function":
- case "symbol":
- case "undefined":
- return [["", ""], [""]];
- }
-
- if (element === null) return [["", ""], [""]];
-
- const { attributeMap = {} } = options;
-
- const componentApi = _componentApi
- ? ComponentApi.clone(_componentApi)
- : ComponentApi.create({ attributeMap });
-
- assertSyncElem(element);
-
- if (element.type === "textNode") {
- return [["", ""], [element.text]];
- }
-
- if (typeof element.tag !== "string") {
- if (ErrorBoundary._isErrorBoundary(element.tag)) {
- const boundary = new element.tag(element.props);
-
- try {
- const subElem = boundary.render(
- element.props,
- componentApi,
- ) as any as JSXTE.SyncElement;
-
- if (subElem instanceof Promise) {
- throw new Error(
- `Encountered an async Component: [${element.tag.name}.render] Asynchronous Component's cannot be parsed by rendertoHTML. If you wante to use asynchronous components use renderToHtmlAsync instead.`,
- );
- }
-
- return jsxElemToTagFuncArgsSync(subElem, options, componentApi);
- } catch (e) {
- const fallbackElem = boundary.onError(
- e,
- element.props,
- componentApi,
- ) as any as JSXTE.SyncElement;
-
- if (fallbackElem instanceof Promise) {
- throw new Error(
- `Encountered an async Component: [${element.tag.name}.onError] Asynchronous Component's cannot be parsed by rendertoHTML. If you wante to use asynchronous components use renderToHtmlAsync instead.`,
- );
- }
-
- return jsxElemToTagFuncArgsSync(fallbackElem, options, componentApi);
- }
- }
-
- if (Interpolate._isInterpolate(element.tag)) {
- const results: TagFunctionArgs = [[], []];
-
- results[0].push("", "");
- results[1].push(element.props.children);
-
- return results;
- }
-
- if (InterpolateTag._isInterpolateRender(element.tag)) {
- const results: TagFunctionArgs = [[], []];
-
- const [tmpTsa, params] = jsxElemToTagFuncArgsSync(
- createElement("", element.props),
- options,
- );
- const templateStringArray = toTemplateStringArray(tmpTsa);
-
- results[0].push("", "");
- results[1].push(options.tag(templateStringArray, ...params));
-
- return results;
- }
-
- const subElem = element.tag(
- element.props,
- componentApi,
- ) as any as JSXTE.SyncElement;
-
- if (subElem instanceof Promise) {
- throw new Error(
- `Encountered an async Component: [${element.tag.name}] Asynchronous Component's cannot be parsed by rendertoHTML. If you wante to use asynchronous components use renderToHtmlAsync instead.`,
- );
- }
-
- return jsxElemToTagFuncArgsSync(subElem, options, componentApi);
- } else {
- const { attributes, children } = resolveElement(element);
-
- if (element.tag === "") {
- const results: TagFunctionArgs = [[], []];
-
- for (let i = 0; i < children.length; i++) {
- const child = children[i]!;
- const [[first, ...strings], tagParams] = jsxElemToTagFuncArgsSync(
- child,
- options,
- componentApi,
- );
-
- concatToLastStringOrPush(results, first);
-
- results[0] = results[0].concat(strings);
- results[1] = results[1].concat(tagParams);
- }
-
- return results;
- } else {
- const isSelfClosingTag =
- children.length === 0 && SELF_CLOSING_TAG_LIST.includes(element.tag);
-
- const results: TagFunctionArgs = [[], []];
-
- results[0].push(`<${element.tag}`);
-
- const attrList = Object.entries(attributes);
- for (let index = 0; index < attrList.length; index++) {
- const [attrName, value] = attrList[index]!;
-
- if (value === false || value === null || value === undefined) {
- continue;
- }
-
- concatToLastStringOrPush(
- results,
- ` ${mapAttributeName(attrName, attributeMap)}="`,
- );
-
- results[1].push(value === true ? attrName : value);
- results[0].push('"');
- }
-
- if (isSelfClosingTag) {
- concatToLastStringOrPush(results, "/>");
- } else {
- concatToLastStringOrPush(results, ">");
-
- for (let i = 0; i < children.length; i++) {
- const child = children[i]!;
- const [[first, ...strings], tagParams] = jsxElemToTagFuncArgsSync(
- child,
- options,
- componentApi,
- );
-
- concatToLastStringOrPush(results, first);
-
- results[0] = results[0].concat(strings);
- results[1] = results[1].concat(tagParams);
- }
-
- concatToLastStringOrPush(results, `${element.tag}>`);
- }
-
- return results;
- }
- }
-};
diff --git a/src/string-template-renderer/render-to-string-template-tag.ts b/src/string-template-renderer/render-to-string-template-tag.ts
deleted file mode 100644
index 4a58b0f..0000000
--- a/src/string-template-renderer/render-to-string-template-tag.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/* eslint-disable max-len */
-import { jsxElemToTagFuncArgsSync } from "./jsx-elem-to-strings";
-import type { StringTemplateTag } from "./string-template-tag-type";
-import { toTemplateStringArray } from "./to-template-string-array";
-
-export interface TemplateLiteralCache {
- get: (
- templateStringsArray: TemplateStringsArray,
- ) => TemplateStringsArray | undefined;
- set: (templateStringsArray: TemplateStringsArray) => void;
-}
-
-export type StringTemplateParserOptions = {
- /**
- * Mappings for html attribute names. Attributes defined in here
- * will get renamed during the rendering to whatever is set.
- *
- * @example
- * const options = {
- * attributeMap: { onclick: "@click" },
- * };
- *
- * renderToStringTemplateTag(
- * html,
- * ,
- * options
- * );
- * // Will give the same result as
- * html``;
- */
- attributeMap?: Record;
- /**
- * Template literal tags have a specific behavior to them, the
- * `TemplateStringsArray` is memoized for evaluations of the same
- * template literal. This is possible because the
- * `TemplateStringsArray` contains only parts of the template
- * literal that are not dynamic, and will never change.
- *
- * JSXTE templates can produce different `TemplateStringsArray` so
- * similar behavior is not possible.
- *
- * By providing a cache object you can simulate a similar behavior
- * to real template literals. Cache object is expected to be an
- * object with a `get` and `set` method.
- *
- * The `get` method shall compare the given `TemplateStringsArray`
- * to the ones in the cache and if a value that is deeply equal to
- * it exists, return it.
- *
- * The `set` method shall store the given `TemplateStringsArray` in
- * the cache.
- */
- cache?: TemplateLiteralCache;
-};
-
-export const renderToStringTemplateTag = (
- tag: StringTemplateTag,
- Component: JSX.Element,
- options: StringTemplateParserOptions = {},
-) => {
- const [tmpTsa, params] = jsxElemToTagFuncArgsSync(Component, {
- ...options,
- tag,
- });
-
- const templateStringArray = toTemplateStringArray(tmpTsa);
-
- const cached = options?.cache?.get(templateStringArray as any);
-
- if (cached) {
- return tag(cached as any, ...params);
- }
-
- options?.cache?.set(templateStringArray as any);
-
- return tag(templateStringArray as any, ...params);
-};
diff --git a/src/string-template-renderer/resolve-element.ts b/src/string-template-renderer/resolve-element.ts
deleted file mode 100644
index a127b42..0000000
--- a/src/string-template-renderer/resolve-element.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-type GetArrayFromUnion = Exclude>>;
-type GetNonArraysFromUnion = Exclude>;
-
-type ArrayType = A extends Array ? T : never;
-
-type AsArray = [GetArrayFromUnion] extends [never]
- ? GetNonArraysFromUnion[]
- : Array> | GetNonArraysFromUnion>;
-
-export function asArray(v: T): AsArray {
- if (Array.isArray(v)) {
- return v;
- } else {
- return [v] as AsArray;
- }
-}
-
-export const resolveElement = (element: JSXTE.TagElement) => {
- const { children, ...attributes } = element.props;
-
- return {
- attributes,
- children: asArray(children ?? []).flat() as JSX.Element[],
- };
-};
diff --git a/src/string-template-renderer/string-template-tag-type.ts b/src/string-template-renderer/string-template-tag-type.ts
deleted file mode 100644
index b40f34b..0000000
--- a/src/string-template-renderer/string-template-tag-type.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export type StringTemplateTag = (
- template: TemplateStringsArray,
- ...arg: any[]
-) => R;
diff --git a/src/string-template-renderer/to-template-string-array.ts b/src/string-template-renderer/to-template-string-array.ts
deleted file mode 100644
index 16bb124..0000000
--- a/src/string-template-renderer/to-template-string-array.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export const toTemplateStringArray = (
- orgArr: string[],
-): TemplateStringsArray => {
- const templateStringArray =
- orgArr.slice() as any as TemplateStringsArray;
-
- Object.defineProperty(templateStringArray, "raw", {
- value: orgArr,
- configurable: false,
- enumerable: false,
- writable: false,
- });
- Object.freeze(templateStringArray);
-
- return templateStringArray;
-};
diff --git a/src/string-template-renderer/map-attribute-name.ts b/src/utilities/map-attribute-name.ts
similarity index 85%
rename from src/string-template-renderer/map-attribute-name.ts
rename to src/utilities/map-attribute-name.ts
index 82e1c99..4d352ed 100644
--- a/src/string-template-renderer/map-attribute-name.ts
+++ b/src/utilities/map-attribute-name.ts
@@ -1,6 +1,6 @@
export const mapAttributeName = (
attributeName: string,
- map: Record
+ map: Record,
): string => {
if (attributeName in map && map[attributeName]) {
return map[attributeName]!;