diff --git a/lib/lang.d.ts b/lib/lang.d.ts index 1f6e3c3..e7d619e 100644 --- a/lib/lang.d.ts +++ b/lib/lang.d.ts @@ -3,3 +3,5 @@ export declare type Nullable = T | null; export declare type Nilable = T | Nil; export declare type Nil = null | undefined; export declare function isBool(v: unknown): v is boolean; +export declare function isStr(v: unknown): v is string; +export declare function intoArr(v: T | T[]): T[]; diff --git a/lib/lang.mjs b/lib/lang.mjs index f35bd8c..70e556a 100644 --- a/lib/lang.mjs +++ b/lib/lang.mjs @@ -4,3 +4,9 @@ export function isNil(v) { export function isBool(v) { return typeof v === "boolean"; } +export function isStr(v) { + return typeof v === "string"; +} +export function intoArr(v) { + return Array.isArray(v) ? v : [v]; +} diff --git a/lib/nodes.d.ts b/lib/nodes.d.ts index 4cd4ce8..11c1606 100644 --- a/lib/nodes.d.ts +++ b/lib/nodes.d.ts @@ -1,26 +1,28 @@ import { Nil, Nilable } from "./lang.js"; -export declare type AnyNode = TextNode | Elem | Frag | Nil | false; -export declare class TextNode extends String { -} +export declare function Et(tagName: string, ...texts: string[]): Elem; +export declare function Ea(tagName: string, attrs?: ElemAttrs, children?: AnyNode | AnyNode[]): Elem; +export declare function E(tagName: string, children: AnyNode | AnyNode[]): Elem; export declare function F(...children: AnyNode[]): Frag; +export declare type AnyNode = TextNode | Elem | Frag | Nil | false; +export declare type TextNode = string; export declare class Frag { #private; constructor(); get children(): Nilable; - withText(text: string): this; - addText(text: string): void; - withChildren(...nodes: AnyNode[]): this; + withText(texts: TextNode | TextNode[]): this; + addText(texts: TextNode | TextNode[]): void; + withChildren(nodes: AnyNode | AnyNode[]): this; + addChildren(nodes: AnyNode | AnyNode[]): void; addChild(node: AnyNode): void; } -export declare function E(tagName: string, attrs: ElemAttrs, ...children: AnyNode[]): Elem; export declare type ElemAttrs = Record; export declare class Elem extends Frag { #private; constructor(tagName: string); get tagName(): string; get attrs(): Record; - withAttrs(attrs: Record): Elem; - withAttr(name: string, value: unknown): Elem; + withAttrs(attrs: ElemAttrs): Elem; + addAttrs(attrs: ElemAttrs): void; addAttr(name: string, value: unknown): void; addChild(node: AnyNode): void; } diff --git a/lib/nodes.mjs b/lib/nodes.mjs index deaafa2..540f47e 100644 --- a/lib/nodes.mjs +++ b/lib/nodes.mjs @@ -1,8 +1,20 @@ -import { isNil } from "./lang.mjs"; -export class TextNode extends String { +import { intoArr, isNil } from "./lang.mjs"; +export function Et(tagName, ...texts) { + return new Elem(tagName).withText(texts); +} +export function Ea(tagName, attrs, children) { + const el = new Elem(tagName); + if (attrs) + el.addAttrs(attrs); + if (children) + el.addChildren(children); + return el; +} +export function E(tagName, children) { + return new Elem(tagName).withChildren(children); } export function F(...children) { - return new Frag().withChildren(...children); + return new Frag().withChildren(children); } export class Frag { #children; @@ -12,26 +24,28 @@ export class Frag { get children() { return this.#children; } - withText(text) { - this.addText(text); + withText(texts) { + this.addText(texts); return this; } - addText(text) { - this.addChild(new TextNode(text)); + addText(texts) { + this.addChildren(intoArr(texts) + .map((t) => t.trim()) + .join(" ")); } - withChildren(...nodes) { - nodes.forEach((n) => this.addChild(n)); + withChildren(nodes) { + this.addChildren(nodes); return this; } + addChildren(nodes) { + intoArr(nodes).forEach((n) => this.addChild(n)); + } addChild(node) { if (isNil(this.#children)) this.#children = []; this.#children.push(node); } } -export function E(tagName, attrs, ...children) { - return new Elem(tagName).withAttrs(attrs).withChildren(...children); -} export class Elem extends Frag { #tagName; #attrs; @@ -49,12 +63,11 @@ export class Elem extends Frag { return this.#attrs; } withAttrs(attrs) { - Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); + this.addAttrs(attrs); return this; } - withAttr(name, value) { - this.addAttr(name, value); - return this; + addAttrs(attrs) { + Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); } addAttr(name, value) { this.#attrs[name] = value; diff --git a/lib/str.mjs b/lib/str.mjs index 6adb48b..f12a0ad 100644 --- a/lib/str.mjs +++ b/lib/str.mjs @@ -1,16 +1,12 @@ -import { isBool, isNil } from "./lang.mjs"; -import { Elem, TextNode } from "./nodes.mjs"; +import { isBool, isNil, isStr } from "./lang.mjs"; +import { Elem } from "./nodes.mjs"; export class StrRenderer { render(node) { return encodeNode(node); } } function encodeAnyNode(node) { - return !node - ? null - : node instanceof TextNode - ? encodeTextNode(node) - : encodeNode(node); + return !node ? null : isStr(node) ? encodeTextNode(node) : encodeNode(node); } function encodeTextNode(node) { return String(node); diff --git a/src/lang.mts b/src/lang.mts index c4d0c68..ac6c013 100644 --- a/src/lang.mts +++ b/src/lang.mts @@ -11,3 +11,11 @@ export type Nil = null | undefined; export function isBool(v: unknown): v is boolean { return typeof v === "boolean"; } + +export function isStr(v: unknown): v is string { + return typeof v === "string"; +} + +export function intoArr(v: T | T[]): T[] { + return Array.isArray(v) ? v : [v]; +} diff --git a/src/nodes.mts b/src/nodes.mts index bed0b45..937a758 100644 --- a/src/nodes.mts +++ b/src/nodes.mts @@ -1,13 +1,31 @@ -import { isNil, Nil, Nilable } from "./lang.mjs"; +import { intoArr, isNil, Nil, Nilable } from "./lang.mjs"; -export type AnyNode = TextNode | Elem | Frag | Nil | false; +export function Et(tagName: string, ...texts: string[]): Elem { + return new Elem(tagName).withText(texts); +} -export class TextNode extends String {} +export function Ea( + tagName: string, + attrs?: ElemAttrs, + children?: AnyNode | AnyNode[] +): Elem { + const el = new Elem(tagName); + if (attrs) el.addAttrs(attrs); + if (children) el.addChildren(children); + return el; +} + +export function E(tagName: string, children: AnyNode | AnyNode[]): Elem { + return new Elem(tagName).withChildren(children); +} export function F(...children: AnyNode[]): Frag { - return new Frag().withChildren(...children); + return new Frag().withChildren(children); } +export type AnyNode = TextNode | Elem | Frag | Nil | false; +export type TextNode = string; + export class Frag { #children: Nilable; @@ -19,34 +37,34 @@ export class Frag { return this.#children; } - withText(text: string): this { - this.addText(text); + withText(texts: TextNode | TextNode[]): this { + this.addText(texts); return this; } - addText(text: string): void { - this.addChild(new TextNode(text)); + addText(texts: TextNode | TextNode[]): void { + this.addChildren( + intoArr(texts) + .map((t) => t.trim()) + .join(" ") + ); } - withChildren(...nodes: AnyNode[]): this { - nodes.forEach((n) => this.addChild(n)); + withChildren(nodes: AnyNode | AnyNode[]): this { + this.addChildren(nodes); return this; } + addChildren(nodes: AnyNode | AnyNode[]): void { + intoArr(nodes).forEach((n) => this.addChild(n)); + } + addChild(node: AnyNode): void { if (isNil(this.#children)) this.#children = []; this.#children.push(node); } } -export function E( - tagName: string, - attrs: ElemAttrs, - ...children: AnyNode[] -): Elem { - return new Elem(tagName).withAttrs(attrs).withChildren(...children); -} - export type ElemAttrs = Record; export class Elem extends Frag { @@ -69,14 +87,13 @@ export class Elem extends Frag { return this.#attrs; } - withAttrs(attrs: Record): Elem { - Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); + withAttrs(attrs: ElemAttrs): Elem { + this.addAttrs(attrs); return this; } - withAttr(name: string, value: unknown): Elem { - this.addAttr(name, value); - return this; + addAttrs(attrs: ElemAttrs): void { + Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); } addAttr(name: string, value: unknown): void { diff --git a/src/str.mts b/src/str.mts index ea859a9..81de510 100644 --- a/src/str.mts +++ b/src/str.mts @@ -1,5 +1,5 @@ import { Renderer } from "./types.mjs"; -import { isBool, isNil, Nilable, Nullable } from "./lang.mjs"; +import { isBool, isNil, isStr, Nilable, Nullable } from "./lang.mjs"; import { AnyNode, Elem, ElemAttrs, Frag, TextNode } from "./nodes.mjs"; export class StrRenderer implements Renderer { @@ -9,11 +9,7 @@ export class StrRenderer implements Renderer { } function encodeAnyNode(node: AnyNode): Nullable { - return !node - ? null - : node instanceof TextNode - ? encodeTextNode(node) - : encodeNode(node); + return !node ? null : isStr(node) ? encodeTextNode(node) : encodeNode(node); } function encodeTextNode(node: TextNode): string {