Archived
1
0
Fork 0

feat: improve api

This commit is contained in:
Dmitriy Pleshevskiy 2022-03-20 23:22:43 +03:00
parent f7d94d9010
commit 1b21c2d796
8 changed files with 101 additions and 61 deletions

2
lib/lang.d.ts vendored
View file

@ -3,3 +3,5 @@ export declare type Nullable<T> = T | null;
export declare type Nilable<T> = T | Nil; export declare type Nilable<T> = T | Nil;
export declare type Nil = null | undefined; export declare type Nil = null | undefined;
export declare function isBool(v: unknown): v is boolean; export declare function isBool(v: unknown): v is boolean;
export declare function isStr(v: unknown): v is string;
export declare function intoArr<T>(v: T | T[]): T[];

View file

@ -4,3 +4,9 @@ export function isNil(v) {
export function isBool(v) { export function isBool(v) {
return typeof v === "boolean"; return typeof v === "boolean";
} }
export function isStr(v) {
return typeof v === "string";
}
export function intoArr(v) {
return Array.isArray(v) ? v : [v];
}

20
lib/nodes.d.ts vendored
View file

@ -1,26 +1,28 @@
import { Nil, Nilable } from "./lang.js"; import { Nil, Nilable } from "./lang.js";
export declare type AnyNode = TextNode | Elem | Frag | Nil | false; export declare function Et(tagName: string, ...texts: string[]): Elem;
export declare class TextNode extends String { 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 function F(...children: AnyNode[]): Frag;
export declare type AnyNode = TextNode | Elem | Frag | Nil | false;
export declare type TextNode = string;
export declare class Frag { export declare class Frag {
#private; #private;
constructor(); constructor();
get children(): Nilable<AnyNode[]>; get children(): Nilable<AnyNode[]>;
withText(text: string): this; withText(texts: TextNode | TextNode[]): this;
addText(text: string): void; addText(texts: TextNode | TextNode[]): void;
withChildren(...nodes: AnyNode[]): this; withChildren(nodes: AnyNode | AnyNode[]): this;
addChildren(nodes: AnyNode | AnyNode[]): void;
addChild(node: AnyNode): void; addChild(node: AnyNode): void;
} }
export declare function E(tagName: string, attrs: ElemAttrs, ...children: AnyNode[]): Elem;
export declare type ElemAttrs = Record<string, unknown>; export declare type ElemAttrs = Record<string, unknown>;
export declare class Elem extends Frag { export declare class Elem extends Frag {
#private; #private;
constructor(tagName: string); constructor(tagName: string);
get tagName(): string; get tagName(): string;
get attrs(): Record<string, unknown>; get attrs(): Record<string, unknown>;
withAttrs(attrs: Record<string, unknown>): Elem; withAttrs(attrs: ElemAttrs): Elem;
withAttr(name: string, value: unknown): Elem; addAttrs(attrs: ElemAttrs): void;
addAttr(name: string, value: unknown): void; addAttr(name: string, value: unknown): void;
addChild(node: AnyNode): void; addChild(node: AnyNode): void;
} }

View file

@ -1,8 +1,20 @@
import { isNil } from "./lang.mjs"; import { intoArr, isNil } from "./lang.mjs";
export class TextNode extends String { 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) { export function F(...children) {
return new Frag().withChildren(...children); return new Frag().withChildren(children);
} }
export class Frag { export class Frag {
#children; #children;
@ -12,26 +24,28 @@ export class Frag {
get children() { get children() {
return this.#children; return this.#children;
} }
withText(text) { withText(texts) {
this.addText(text); this.addText(texts);
return this; return this;
} }
addText(text) { addText(texts) {
this.addChild(new TextNode(text)); this.addChildren(intoArr(texts)
.map((t) => t.trim())
.join(" "));
} }
withChildren(...nodes) { withChildren(nodes) {
nodes.forEach((n) => this.addChild(n)); this.addChildren(nodes);
return this; return this;
} }
addChildren(nodes) {
intoArr(nodes).forEach((n) => this.addChild(n));
}
addChild(node) { addChild(node) {
if (isNil(this.#children)) if (isNil(this.#children))
this.#children = []; this.#children = [];
this.#children.push(node); this.#children.push(node);
} }
} }
export function E(tagName, attrs, ...children) {
return new Elem(tagName).withAttrs(attrs).withChildren(...children);
}
export class Elem extends Frag { export class Elem extends Frag {
#tagName; #tagName;
#attrs; #attrs;
@ -49,12 +63,11 @@ export class Elem extends Frag {
return this.#attrs; return this.#attrs;
} }
withAttrs(attrs) { withAttrs(attrs) {
Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); this.addAttrs(attrs);
return this; return this;
} }
withAttr(name, value) { addAttrs(attrs) {
this.addAttr(name, value); Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value));
return this;
} }
addAttr(name, value) { addAttr(name, value) {
this.#attrs[name] = value; this.#attrs[name] = value;

View file

@ -1,16 +1,12 @@
import { isBool, isNil } from "./lang.mjs"; import { isBool, isNil, isStr } from "./lang.mjs";
import { Elem, TextNode } from "./nodes.mjs"; import { Elem } from "./nodes.mjs";
export class StrRenderer { export class StrRenderer {
render(node) { render(node) {
return encodeNode(node); return encodeNode(node);
} }
} }
function encodeAnyNode(node) { function encodeAnyNode(node) {
return !node return !node ? null : isStr(node) ? encodeTextNode(node) : encodeNode(node);
? null
: node instanceof TextNode
? encodeTextNode(node)
: encodeNode(node);
} }
function encodeTextNode(node) { function encodeTextNode(node) {
return String(node); return String(node);

View file

@ -11,3 +11,11 @@ export type Nil = null | undefined;
export function isBool(v: unknown): v is boolean { export function isBool(v: unknown): v is boolean {
return typeof v === "boolean"; return typeof v === "boolean";
} }
export function isStr(v: unknown): v is string {
return typeof v === "string";
}
export function intoArr<T>(v: T | T[]): T[] {
return Array.isArray(v) ? v : [v];
}

View file

@ -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 { 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 { export class Frag {
#children: Nilable<AnyNode[]>; #children: Nilable<AnyNode[]>;
@ -19,34 +37,34 @@ export class Frag {
return this.#children; return this.#children;
} }
withText(text: string): this { withText(texts: TextNode | TextNode[]): this {
this.addText(text); this.addText(texts);
return this; return this;
} }
addText(text: string): void { addText(texts: TextNode | TextNode[]): void {
this.addChild(new TextNode(text)); this.addChildren(
intoArr(texts)
.map((t) => t.trim())
.join(" ")
);
} }
withChildren(...nodes: AnyNode[]): this { withChildren(nodes: AnyNode | AnyNode[]): this {
nodes.forEach((n) => this.addChild(n)); this.addChildren(nodes);
return this; return this;
} }
addChildren(nodes: AnyNode | AnyNode[]): void {
intoArr(nodes).forEach((n) => this.addChild(n));
}
addChild(node: AnyNode): void { addChild(node: AnyNode): void {
if (isNil(this.#children)) this.#children = []; if (isNil(this.#children)) this.#children = [];
this.#children.push(node); 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<string, unknown>; export type ElemAttrs = Record<string, unknown>;
export class Elem extends Frag { export class Elem extends Frag {
@ -69,14 +87,13 @@ export class Elem extends Frag {
return this.#attrs; return this.#attrs;
} }
withAttrs(attrs: Record<string, unknown>): Elem { withAttrs(attrs: ElemAttrs): Elem {
Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); this.addAttrs(attrs);
return this; return this;
} }
withAttr(name: string, value: unknown): Elem { addAttrs(attrs: ElemAttrs): void {
this.addAttr(name, value); Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value));
return this;
} }
addAttr(name: string, value: unknown): void { addAttr(name: string, value: unknown): void {

View file

@ -1,5 +1,5 @@
import { Renderer } from "./types.mjs"; 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"; import { AnyNode, Elem, ElemAttrs, Frag, TextNode } from "./nodes.mjs";
export class StrRenderer implements Renderer<string> { export class StrRenderer implements Renderer<string> {
@ -9,11 +9,7 @@ export class StrRenderer implements Renderer<string> {
} }
function encodeAnyNode(node: AnyNode): Nullable<string> { function encodeAnyNode(node: AnyNode): Nullable<string> {
return !node return !node ? null : isStr(node) ? encodeTextNode(node) : encodeNode(node);
? null
: node instanceof TextNode
? encodeTextNode(node)
: encodeNode(node);
} }
function encodeTextNode(node: TextNode): string { function encodeTextNode(node: TextNode): string {