@vimpak/jsx 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ export interface ContextToken<T> {
2
+ key: symbol;
3
+ factory: () => T;
4
+ }
5
+ export declare function createContextToken<T>(factory: () => T): ContextToken<T>;
6
+ export declare class Context {
7
+ #private;
8
+ private constructor();
9
+ get<T>(token: ContextToken<T>): T;
10
+ set<T>(token: ContextToken<T>, value: T): Context;
11
+ reset<T>(token: ContextToken<T>): Context;
12
+ }
@@ -0,0 +1,37 @@
1
+ export function createContextToken(factory) {
2
+ return { factory, key: Symbol() };
3
+ }
4
+ const contextCache = {};
5
+ export class Context {
6
+ #map = {};
7
+ constructor(map) {
8
+ this.#map = map;
9
+ }
10
+ get(token) {
11
+ const items = this.#map[token.key];
12
+ if (!Array.isArray(items) || items.length === 0) {
13
+ if (!(token.key in contextCache)) {
14
+ contextCache[token.key] = token.factory();
15
+ }
16
+ return contextCache[token.key];
17
+ }
18
+ return items[items.length - 1].value;
19
+ }
20
+ set(token, value) {
21
+ const map = { ...this.#map };
22
+ if (!(token.key in map)) {
23
+ map[token.key] = [{ value }];
24
+ }
25
+ else {
26
+ map[token.key].push({ value });
27
+ }
28
+ return new Context(map);
29
+ }
30
+ reset(token) {
31
+ const map = { ...this.#map };
32
+ if (token.key in map) {
33
+ map[token.key].pop();
34
+ }
35
+ return new Context(map);
36
+ }
37
+ }
package/dist/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function stringifyStyle(style: object | string, parts: string[]): void;
package/dist/css.js ADDED
@@ -0,0 +1,26 @@
1
+ const hyphenateRegex = /[A-Z]|^ms/g;
2
+ const styleNames = new Map();
3
+ function processStyleName(name) {
4
+ if (name.charCodeAt(1) === 45) {
5
+ return name;
6
+ }
7
+ let out = styleNames.get(name);
8
+ if (!out) {
9
+ out = name.replace(hyphenateRegex, "-$&").toLowerCase();
10
+ styleNames.set(name, out);
11
+ }
12
+ return out;
13
+ }
14
+ export function stringifyStyle(style, parts) {
15
+ parts.push(` style="`);
16
+ if (typeof style === "string") {
17
+ parts.push(style);
18
+ }
19
+ else {
20
+ const css = style;
21
+ for (const key in css) {
22
+ parts.push(processStyleName(key), ":", css[key]);
23
+ }
24
+ }
25
+ parts.push(`"`);
26
+ }
@@ -0,0 +1 @@
1
+ export { render } from "./render";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { render } from "./render";
@@ -0,0 +1 @@
1
+ export { Fragment, jsxDEV } from "./jsx";
@@ -0,0 +1 @@
1
+ export { Fragment, jsxDEV } from "./jsx";
@@ -0,0 +1 @@
1
+ export { Fragment, jsx, jsxs } from "./jsx";
@@ -0,0 +1 @@
1
+ export { Fragment, jsx, jsxs } from "./jsx";
package/dist/jsx.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ export declare const Fragment: unique symbol;
2
+ export type KiteKey = string | number | bigint;
3
+ export type KiteFC<P> = (props: P, ctx: Kite.Context) => KiteNode | Promise<KiteNode>;
4
+ export type KiteElementType<P = unknown, Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements> = string | KiteFC<P> | typeof Fragment | {
5
+ [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never;
6
+ }[Tag];
7
+ export type KiteElement<P = unknown, Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements> = {
8
+ props: P;
9
+ $$typeof: symbol;
10
+ key: string | null;
11
+ type: KiteElementType<P, Tag>;
12
+ };
13
+ export type KiteNode = null | boolean | number | string | KiteElement | KiteNode[];
14
+ export declare function jsxDEV(type: KiteElementType, props: unknown, key: undefined | KiteKey): KiteElement;
15
+ export declare function jsx(type: KiteElementType, props: unknown, key: undefined | KiteKey): KiteElement;
16
+ export declare function jsxs(type: KiteElementType, props: unknown, key: undefined | KiteKey): KiteElement;
17
+ declare global {
18
+ namespace Kite {
19
+ interface Context {
20
+ }
21
+ }
22
+ namespace JSX {
23
+ type Element = KiteElement;
24
+ type ElementType = keyof IntrinsicElements | KiteFC<any>;
25
+ interface IntrinsicElements {
26
+ [tag: string]: HtmlTag;
27
+ }
28
+ interface IntrinsicAttributes {
29
+ children?: KiteNode;
30
+ }
31
+ }
32
+ }
package/dist/jsx.js ADDED
@@ -0,0 +1,33 @@
1
+ export const Fragment = Symbol("fragment");
2
+ const $$typeof = Symbol("");
3
+ export function jsxDEV(type, props, key) {
4
+ return {
5
+ type,
6
+ props,
7
+ $$typeof,
8
+ key: buildKey(key),
9
+ };
10
+ }
11
+ export function jsx(type, props, key) {
12
+ return {
13
+ type,
14
+ props,
15
+ $$typeof,
16
+ key: buildKey(key),
17
+ };
18
+ }
19
+ export function jsxs(type, props, key) {
20
+ return {
21
+ type,
22
+ props,
23
+ $$typeof,
24
+ key: buildKey(key),
25
+ };
26
+ }
27
+ function buildKey(key) {
28
+ if (key === undefined)
29
+ return null;
30
+ if (typeof key === "string")
31
+ return key;
32
+ return key.toString();
33
+ }
@@ -0,0 +1,2 @@
1
+ import { KiteNode } from "./jsx";
2
+ export declare function render(el: KiteNode, ctx?: null | Kite.Context): Promise<string>;
package/dist/render.js ADDED
@@ -0,0 +1,106 @@
1
+ import { escapeHTML } from "bun";
2
+ import { Fragment } from "./jsx";
3
+ import { stringifyStyle } from "./css";
4
+ const escape = escapeHTML;
5
+ function renderNode(el, parts, ctx) {
6
+ if (typeof el === "string")
7
+ return parts.push(escape(el));
8
+ if (typeof el === "number")
9
+ return parts.push(el.toString());
10
+ if (el === true || !el)
11
+ return;
12
+ if (Array.isArray(el)) {
13
+ const length = el.length;
14
+ for (let i = 0; i < length; i++) {
15
+ renderNode(el[i], parts, ctx);
16
+ }
17
+ return;
18
+ }
19
+ const ke = el;
20
+ if (ke.type === Fragment) {
21
+ if (ke.props.children)
22
+ renderNode(ke.props.children, parts, ctx);
23
+ return;
24
+ }
25
+ if (typeof ke.type === "string")
26
+ return renderHtml(ke, parts, ctx);
27
+ if (typeof ke.type === "function") {
28
+ const it = ke.type(el.props, ctx);
29
+ if (it instanceof Promise)
30
+ return it.then((it) => renderNode(it, parts, ctx));
31
+ return renderNode(it, parts, ctx);
32
+ }
33
+ throw new Error("unknown type of element");
34
+ }
35
+ function renderHtml(el, parts, ctx) {
36
+ if (typeof el.type !== "string")
37
+ throw new Error();
38
+ const props = el.props;
39
+ parts.push("<", el.type);
40
+ propsToString(props, parts);
41
+ if (isVoidElement[el.type])
42
+ return parts.push("/>");
43
+ parts.push(">");
44
+ if (typeof props.safe === "string") {
45
+ if (typeof props.children !== "undefined") {
46
+ throw new Error("can not use children with safe");
47
+ }
48
+ parts.push(props.safe);
49
+ }
50
+ else {
51
+ const children = props.children;
52
+ if (typeof children !== "undefined")
53
+ renderNode(children, parts, ctx);
54
+ }
55
+ parts.push("</", el.type, ">");
56
+ }
57
+ function propsToString(props, parts) {
58
+ const keys = Object.keys(props);
59
+ const values = props;
60
+ const length = keys.length;
61
+ for (let i = 0; i < length; i++) {
62
+ const key = keys[i];
63
+ if (key === "children" || key === "safe")
64
+ continue;
65
+ const value = values[key];
66
+ if (!value)
67
+ continue;
68
+ if (key === "style") {
69
+ stringifyStyle(value, parts);
70
+ continue;
71
+ }
72
+ if (typeof value === "boolean") {
73
+ parts.push(" ", escape(key));
74
+ continue;
75
+ }
76
+ if (typeof value === "string") {
77
+ parts.push(" ", escape(key), `="`, escape(value), `"`);
78
+ }
79
+ else {
80
+ parts.push(" ", escape(key), `="`, escape(value.toString()), `"`);
81
+ }
82
+ }
83
+ }
84
+ export async function render(el, ctx = null) {
85
+ const parts = [];
86
+ await renderNode(el, parts, ctx);
87
+ return parts.join("");
88
+ }
89
+ const isVoidElement = {
90
+ meta: true,
91
+ link: true,
92
+ img: true,
93
+ br: true,
94
+ input: true,
95
+ hr: true,
96
+ area: true,
97
+ base: true,
98
+ col: true,
99
+ command: true,
100
+ embed: true,
101
+ keygen: true,
102
+ param: true,
103
+ source: true,
104
+ track: true,
105
+ wbr: true,
106
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "src/jsx-runtime";
2
+ import { describe, expect, test } from "bun:test";
3
+ import { render } from "./render";
4
+ describe("render", () => {
5
+ test("null", async () => {
6
+ expect(await render(null)).toBe("");
7
+ });
8
+ test("boolean", async () => {
9
+ expect(await render(true)).toBe("");
10
+ expect(await render(false)).toBe("");
11
+ });
12
+ test("number", async () => {
13
+ expect(await render(0)).toBe("0");
14
+ expect(await render(1000)).toBe("1000");
15
+ });
16
+ test("string", async () => {
17
+ expect(await render("")).toBe("");
18
+ expect(await render("hello world")).toBe("hello world");
19
+ });
20
+ test("array", async () => {
21
+ expect(await render([1, 2, 3])).toBe("123");
22
+ });
23
+ test("fragment", async () => {
24
+ expect(await render(_jsx(_Fragment, {}))).toBe("");
25
+ expect(await render(_jsx(_Fragment, { children: "123" }))).toBe("123");
26
+ });
27
+ describe("element", () => {
28
+ test("image", async () => {
29
+ expect(await render(_jsx("img", { src: "test.png" }))).toBe(`<img src="test.png"/>`);
30
+ });
31
+ test("escape", async () => {
32
+ expect(await render(_jsx("div", { children: "\"abc\"" }))).toBe(`<div>&quot;abc&quot;</div>`);
33
+ });
34
+ test("safe", async () => {
35
+ expect(await render(_jsx("div", { safe: `"abc"` }))).toBe(`<div>"abc"</div>`);
36
+ });
37
+ });
38
+ describe("function", () => {
39
+ function Test() {
40
+ return "Hello World";
41
+ }
42
+ test("01", async () => {
43
+ expect(await render(_jsx(Test, {}))).toBe("Hello World");
44
+ });
45
+ test("02", async () => {
46
+ async function TestAsync() {
47
+ return "Hello World";
48
+ }
49
+ expect(await render(_jsx(TestAsync, {}))).toBe("Hello World");
50
+ });
51
+ test("03", async () => {
52
+ expect(await render(_jsxs("div", { children: ["Test", _jsx(Test, {})] }))).toBe("<div>TestHello World</div>");
53
+ });
54
+ test("03", async () => {
55
+ class Context {
56
+ }
57
+ const ctx = new Context();
58
+ function Test(props, _ctx) {
59
+ expect(ctx).toBe(_ctx);
60
+ return "Hello " + props.name;
61
+ }
62
+ expect(await render(_jsx(Test, { name: "World" }), ctx)).toBe("Hello World");
63
+ });
64
+ });
65
+ });
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@vimpak/jsx",
4
+ "collaborators": [
5
+ "Sudesh Yadav <sudeshyadav955@gmail.com>"
6
+ ],
7
+ "files": [
8
+ "./dist"
9
+ ],
10
+ "main": "./dist/index.js",
11
+ "version": "0.1.0",
12
+ "scripts": {
13
+ "prettify": "prettier --write --ignore-unknown ."
14
+ },
15
+ "devDependencies": {
16
+ "prettier": "^3.2.5",
17
+ "typescript": "^5.4.5",
18
+ "vite": "^5.2.11"
19
+ },
20
+ "dependencies": {
21
+ "@types/bun": "^1.3.9",
22
+ "csstype": "^3.1.3"
23
+ }
24
+ }