Archived
1
0
Fork 0

feat: add Nil and false to sync node type

refac: remove maybeWithChildren, use sparse array
This commit is contained in:
Dmitriy Pleshevskiy 2022-03-17 16:34:39 +03:00
parent 9a783eba32
commit f577128923
5 changed files with 25 additions and 30 deletions

9
lib/nodes.d.ts vendored
View file

@ -1,22 +1,21 @@
import { Nilable } from "./lang.js"; import { Nil, Nilable } from "./lang.js";
export declare type AnyNode = AnySyncNode | AnyAsyncNode; export declare type AnyNode = AnySyncNode | AnyAsyncNode;
export declare type AnyAsyncNode = Promise<AnySyncNode>; export declare type AnyAsyncNode = Promise<AnySyncNode>;
export declare type AnySyncNode = TextNode | Elem | Frag; export declare type AnySyncNode = TextNode | Elem | Frag | Nil | false;
export declare class TextNode extends String { export declare class TextNode extends String {
} }
export declare function F(children: AnyNode[]): Frag; export declare function F(...children: AnyNode[]): Frag;
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(text: string): this;
addText(text: string): void; addText(text: string): void;
maybeWithChildren(nodes?: Nilable<AnyNode[]>): this;
withChildren(nodes: AnyNode[]): this; withChildren(nodes: AnyNode[]): this;
withChild(node: AnyNode): this; withChild(node: AnyNode): this;
addChild(node: AnyNode): void; addChild(node: AnyNode): void;
} }
export declare function E(tagName: string, attrs: ElemAttrs, children?: Nilable<AnyNode[]>): Elem; 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;

View file

@ -1,7 +1,7 @@
import { isNil } from "./lang.mjs"; import { isNil } from "./lang.mjs";
export class TextNode extends String { export class TextNode extends String {
} }
export function F(children) { export function F(...children) {
return new Frag().withChildren(children); return new Frag().withChildren(children);
} }
export class Frag { export class Frag {
@ -19,11 +19,6 @@ export class Frag {
addText(text) { addText(text) {
this.addChild(new TextNode(text)); this.addChild(new TextNode(text));
} }
maybeWithChildren(nodes) {
if (isNil(nodes))
return this;
return this.withChildren(nodes);
}
withChildren(nodes) { withChildren(nodes) {
nodes.forEach((n) => this.addChild(n)); nodes.forEach((n) => this.addChild(n));
return this; return this;
@ -38,8 +33,8 @@ export class Frag {
this.#children.push(node); this.#children.push(node);
} }
} }
export function E(tagName, attrs, children) { export function E(tagName, attrs, ...children) {
return new Elem(tagName).withAttrs(attrs).maybeWithChildren(children); return new Elem(tagName).withAttrs(attrs).withChildren(children);
} }
export class Elem extends Frag { export class Elem extends Frag {
#tagName; #tagName;

View file

@ -7,9 +7,11 @@ export class StrRenderer {
} }
async function encodeAnyNode(node) { async function encodeAnyNode(node) {
const syncNode = await node; const syncNode = await node;
return syncNode instanceof TextNode return !syncNode
? encodeTextNode(syncNode) ? null
: encodeNode(syncNode); : syncNode instanceof TextNode
? encodeTextNode(syncNode)
: encodeNode(syncNode);
} }
function encodeTextNode(node) { function encodeTextNode(node) {
return String(node); return String(node);
@ -17,7 +19,7 @@ function encodeTextNode(node) {
async function encodeNode(node) { async function encodeNode(node) {
const encodedChildren = isNil(node.children) const encodedChildren = isNil(node.children)
? undefined ? undefined
: await Promise.all(node.children.map(encodeAnyNode)); : await Promise.all(node.children.map(encodeAnyNode)).then((children) => children.filter((c) => Boolean(c)));
return node instanceof Elem return node instanceof Elem
? encodeHtmlElement(node.tagName, node.attrs, encodedChildren) ? encodeHtmlElement(node.tagName, node.attrs, encodedChildren)
: encodeHtmlFragment(encodedChildren); : encodeHtmlFragment(encodedChildren);

View file

@ -1,12 +1,12 @@
import { isNil, Nilable } from "./lang.mjs"; import { isNil, Nil, Nilable } from "./lang.mjs";
export type AnyNode = AnySyncNode | AnyAsyncNode; export type AnyNode = AnySyncNode | AnyAsyncNode;
export type AnyAsyncNode = Promise<AnySyncNode>; export type AnyAsyncNode = Promise<AnySyncNode>;
export type AnySyncNode = TextNode | Elem | Frag; export type AnySyncNode = TextNode | Elem | Frag | Nil | false;
export class TextNode extends String {} export class TextNode extends String {}
export function F(children: AnyNode[]): Frag { export function F(...children: AnyNode[]): Frag {
return new Frag().withChildren(children); return new Frag().withChildren(children);
} }
@ -30,11 +30,6 @@ export class Frag {
this.addChild(new TextNode(text)); this.addChild(new TextNode(text));
} }
maybeWithChildren(nodes?: Nilable<AnyNode[]>): this {
if (isNil(nodes)) return this;
return this.withChildren(nodes);
}
withChildren(nodes: AnyNode[]): this { withChildren(nodes: AnyNode[]): this {
nodes.forEach((n) => this.addChild(n)); nodes.forEach((n) => this.addChild(n));
return this; return this;
@ -54,9 +49,9 @@ export class Frag {
export function E( export function E(
tagName: string, tagName: string,
attrs: ElemAttrs, attrs: ElemAttrs,
children?: Nilable<AnyNode[]> ...children: AnyNode[]
): Elem { ): Elem {
return new Elem(tagName).withAttrs(attrs).maybeWithChildren(children); return new Elem(tagName).withAttrs(attrs).withChildren(children);
} }
export type ElemAttrs = Record<string, unknown>; export type ElemAttrs = Record<string, unknown>;

View file

@ -8,9 +8,11 @@ export class StrRenderer implements Renderer<string> {
} }
} }
async function encodeAnyNode(node: AnyNode): Promise<string> { async function encodeAnyNode(node: AnyNode): Promise<Nullable<string>> {
const syncNode = await node; const syncNode = await node;
return syncNode instanceof TextNode return !syncNode
? null
: syncNode instanceof TextNode
? encodeTextNode(syncNode) ? encodeTextNode(syncNode)
: encodeNode(syncNode); : encodeNode(syncNode);
} }
@ -22,7 +24,9 @@ function encodeTextNode(node: TextNode): string {
async function encodeNode(node: Elem | Frag): Promise<string> { async function encodeNode(node: Elem | Frag): Promise<string> {
const encodedChildren = isNil(node.children) const encodedChildren = isNil(node.children)
? undefined ? undefined
: await Promise.all(node.children.map(encodeAnyNode)); : await Promise.all(node.children.map(encodeAnyNode)).then((children) =>
children.filter((c): c is string => Boolean(c))
);
return node instanceof Elem return node instanceof Elem
? encodeHtmlElement(node.tagName, node.attrs, encodedChildren) ? encodeHtmlElement(node.tagName, node.attrs, encodedChildren)
: encodeHtmlFragment(encodedChildren); : encodeHtmlFragment(encodedChildren);