From e5506008fb8398cd1e2536cf25ab8f5eda4e11ed Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Sat, 31 Dec 2022 17:17:20 +0100 Subject: [PATCH 01/28] feat: solid-primitives-parser --- packages/parser/LICENSE | 21 ++++++++ packages/parser/README.md | 27 +++++++++++ packages/parser/dev/index.html | 42 ++++++++++++++++ packages/parser/dev/index.tsx | 75 +++++++++++++++++++++++++++++ packages/parser/dev/vite.config.ts | 2 + packages/parser/package.json | 72 +++++++++++++++++++++++++++ packages/parser/src/index.tsx | 46 ++++++++++++++++++ packages/parser/test/index.test.ts | 0 packages/parser/test/server.test.ts | 0 9 files changed, 285 insertions(+) create mode 100644 packages/parser/LICENSE create mode 100644 packages/parser/README.md create mode 100644 packages/parser/dev/index.html create mode 100644 packages/parser/dev/index.tsx create mode 100644 packages/parser/dev/vite.config.ts create mode 100644 packages/parser/package.json create mode 100644 packages/parser/src/index.tsx create mode 100644 packages/parser/test/index.test.ts create mode 100644 packages/parser/test/server.test.ts diff --git a/packages/parser/LICENSE b/packages/parser/LICENSE new file mode 100644 index 000000000..38b41d975 --- /dev/null +++ b/packages/parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Solid Primitives Working Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/parser/README.md b/packages/parser/README.md new file mode 100644 index 000000000..38b12282c --- /dev/null +++ b/packages/parser/README.md @@ -0,0 +1,27 @@ +

+ Solid Primitives parser +

+ +# @solid-primitives/parser + +## Installation + +```bash +npm install @solid-primitives/parser +# or +yarn add @solid-primitives/parser +# or +pnpm add @solid-primitives/parser +``` + +## How to use it + +## TODO + +## Demo + +TODO + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md) diff --git a/packages/parser/dev/index.html b/packages/parser/dev/index.html new file mode 100644 index 000000000..38e477282 --- /dev/null +++ b/packages/parser/dev/index.html @@ -0,0 +1,42 @@ + + + + + + + Solid App + + + + + +
+ + + + diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx new file mode 100644 index 000000000..ec0fddf76 --- /dev/null +++ b/packages/parser/dev/index.tsx @@ -0,0 +1,75 @@ +import { Component, createMemo, For, JSXElement } from "solid-js"; +import { render } from "solid-js/web"; +import "uno.css"; +import { tokenize, Token, childrenTokens } from "../src"; + +type Meta = { + id: "Value" | "Add" | "Subtract"; +}; +type Props = { + value: number; + children?: JSXElement | JSXElement[]; +}; + +type CustomToken = Token; + +const Calculator = (props: { + children: JSXElement | JSXElement[] | CustomToken | CustomToken[]; +}) => { + const tokens = childrenTokens(() => props.children); + + const calculation = () => { + let result = 0; + tokens().forEach(token => { + console.log("token is ", token); + if (token.meta.id === "Value") { + result = token.props.value; + } else if (token.meta.id === "Add") { + result += token.props.value; + } else if (token.meta.id === "Subtract") { + result -= token.props.value; + } + console.log("result is", result); + }); + return result; + }; + + return ( +
+ {token => token.callback()} = {calculation()} +
+ ); +}; + +const Value = tokenize( + (props: Props) => { + return <>{props.value}; + }, + { id: "Value" } +); + +const Add = tokenize( + (props: Props) => { + return <> + {props.value}; + }, + { id: "Add" } +); + +const Subtract = tokenize( + (props: Props) => { + return <> - {props.value}; + }, + { id: "Subtract" } +); + +const App: Component = () => { + return ( + + + + + + ); +}; + +render(() => , document.getElementById("root")!); diff --git a/packages/parser/dev/vite.config.ts b/packages/parser/dev/vite.config.ts new file mode 100644 index 000000000..7ca66a520 --- /dev/null +++ b/packages/parser/dev/vite.config.ts @@ -0,0 +1,2 @@ +import { viteConfig } from "../../../configs/vite.config"; +export default viteConfig; diff --git a/packages/parser/package.json b/packages/parser/package.json new file mode 100644 index 000000000..199b5fbad --- /dev/null +++ b/packages/parser/package.json @@ -0,0 +1,72 @@ +{ + "name": "@solid-primitives/parser", + "version": "0.0.101", + "description": "A primitive to tokenize your solid-components to enable custom parsing.", + "author": "Vincent Van Dijck ", + "contributors": [], + "license": "MIT", + "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/parser#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/solidjs-community/solid-primitives.git" + }, + "bugs": { + "url": "https://github.com/solidjs-community/solid-primitives/issues" + }, + "primitive": { + "name": "parser", + "stage": 0, + "list": [ + "createPagination" + ], + "category": "Utilities" + }, + "keywords": [ + "solid", + "primitives", + "parser" + ], + "private": false, + "sideEffects": false, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/server.cjs", + "module": "./dist/server.js", + "browser": { + "./dist/server.cjs": "./dist/index.cjs", + "./dist/server.js": "./dist/index.js" + }, + "types": "./dist/index.d.ts", + "exports": { + "worker": { + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "browser": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "deno": { + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "node": { + "import": "./dist/server.js", + "require": "./dist/server.cjs" + }, + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "scripts": { + "dev": "vite serve dev", + "build": "jiti ../../scripts/build.ts --ssr", + "test": "vitest -c ../../configs/vitest.config.ts", + "test:ssr": "pnpm run test --mode ssr" + }, + "dependencies": {}, + "peerDependencies": { + "solid-js": "^1.6.0" + } +} diff --git a/packages/parser/src/index.tsx b/packages/parser/src/index.tsx new file mode 100644 index 000000000..c9faf74f5 --- /dev/null +++ b/packages/parser/src/index.tsx @@ -0,0 +1,46 @@ +import { JSXElement, createMemo } from "solid-js"; +import { Dynamic } from "solid-js/web"; + +const $TOKEN = Symbol("solid-parser"); + +export type Token = { + props: T; + meta: U; + component: (props: any) => JSXElement; + callback: () => any; +}; + +export function tokenize( + callback: (props: T) => any, + meta?: { [key: string]: any }, + component?: (props: T) => JSXElement +): (props: T) => JSXElement { + return (props: any) => { + return Object.assign( + () => { + if (!component) + console.info( + "only Tokens tokenized with a component-parameter can be renderered outside a Parser" + ); + return <>} />; + }, + { + props, + [$TOKEN]: true, + meta, + callback: () => callback(props) + } + ); + }; +} + +export function childrenTokens(children: () => JSXElement | JSXElement[] | T | T[]) { + return createMemo(() => + children + ? ([] as any[]) + .concat(children()) + .filter(child => child && $TOKEN in child) + .map((a: T) => a as T) + : [] + ); +} diff --git a/packages/parser/test/index.test.ts b/packages/parser/test/index.test.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/parser/test/server.test.ts b/packages/parser/test/server.test.ts new file mode 100644 index 000000000..e69de29bb From c70d34bd5555620d2fe408779f5092a13dffdaa3 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Mon, 2 Jan 2023 19:43:54 +0100 Subject: [PATCH 02/28] add primitives to list --- packages/parser/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/parser/package.json b/packages/parser/package.json index 199b5fbad..f7659b094 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -17,7 +17,9 @@ "name": "parser", "stage": 0, "list": [ - "createPagination" + "Token", + "tokenize", + "childrenTokens" ], "category": "Utilities" }, From 86aca9c1a3d752e62df2ccd5fa09bd0a3bea0bf4 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Tue, 3 Jan 2023 23:30:33 +0100 Subject: [PATCH 03/28] createParser to scope $TOKEN, spread meta over token instead of having a global $TOKEN, $TOKEN is now scoped to createParser this way multiple parsers can co-exists. token.meta is now spread out over token. this way keys passed in tokenize() can be used to typeguard so we have narrower types while parsing --- packages/parser/dev/index.tsx | 57 +++++++++++++------------- packages/parser/package.json | 3 +- packages/parser/src/index.tsx | 75 +++++++++++++++++------------------ 3 files changed, 67 insertions(+), 68 deletions(-) diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx index ec0fddf76..94b40d7be 100644 --- a/packages/parser/dev/index.tsx +++ b/packages/parser/dev/index.tsx @@ -1,17 +1,17 @@ import { Component, createMemo, For, JSXElement } from "solid-js"; import { render } from "solid-js/web"; import "uno.css"; -import { tokenize, Token, childrenTokens } from "../src"; +import { Token, createParser } from "../src"; -type Meta = { - id: "Value" | "Add" | "Subtract"; -}; +const { tokenize, childrenTokens } = createParser("calculator"); + +type Meta = { callback: (props: Props) => JSXElement }; type Props = { value: number; children?: JSXElement | JSXElement[]; }; -type CustomToken = Token; +type CustomToken = TokenValue | TokenAdd | TokenSubtract; const Calculator = (props: { children: JSXElement | JSXElement[] | CustomToken | CustomToken[]; @@ -22,11 +22,11 @@ const Calculator = (props: { let result = 0; tokens().forEach(token => { console.log("token is ", token); - if (token.meta.id === "Value") { + if (token.id === "Value") { result = token.props.value; - } else if (token.meta.id === "Add") { + } else if (token.id === "Add") { result += token.props.value; - } else if (token.meta.id === "Subtract") { + } else if (token.id === "Subtract") { result -= token.props.value; } console.log("result is", result); @@ -36,31 +36,34 @@ const Calculator = (props: { return (
- {token => token.callback()} = {calculation()} + {token => token.callback(token.props)} = {calculation()}
); }; -const Value = tokenize( - (props: Props) => { - return <>{props.value}; - }, - { id: "Value" } -); +type MetaValue = { + id: "Value"; +} & Meta; +type TokenValue = Token; + +const Value = tokenize({ id: "Value", callback: props => <>{props.value} }); + +type MetaAdd = { + id: "Add"; +} & Meta; +type TokenAdd = Token; + +const Add = tokenize({ id: "Add", callback: (props: Props) => <> + {props.value} }); -const Add = tokenize( - (props: Props) => { - return <> + {props.value}; - }, - { id: "Add" } -); +type MetaSubtract = { + id: "Subtract"; +} & Meta; +type TokenSubtract = Token; -const Subtract = tokenize( - (props: Props) => { - return <> - {props.value}; - }, - { id: "Subtract" } -); +const Subtract = tokenize({ + id: "Subtract", + callback: (props: Props) => <> - {props.value} +}); const App: Component = () => { return ( diff --git a/packages/parser/package.json b/packages/parser/package.json index f7659b094..955a3cf88 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -18,8 +18,7 @@ "stage": 0, "list": [ "Token", - "tokenize", - "childrenTokens" + "createParser" ], "category": "Utilities" }, diff --git a/packages/parser/src/index.tsx b/packages/parser/src/index.tsx index c9faf74f5..7229997f0 100644 --- a/packages/parser/src/index.tsx +++ b/packages/parser/src/index.tsx @@ -1,46 +1,43 @@ import { JSXElement, createMemo } from "solid-js"; -import { Dynamic } from "solid-js/web"; -const $TOKEN = Symbol("solid-parser"); +export function createParser(id?: string) { + const $TOKEN = Symbol(id || "solid-parser"); -export type Token = { - props: T; - meta: U; - component: (props: any) => JSXElement; - callback: () => any; -}; + function tokenize< + TToken extends Meta & { + props: unknown; + } + >(meta: Omit): (props: TToken["props"]) => JSXElement { + return (props: any) => { + return Object.assign( + () => { + console.info("tokens can only be rendered inside a Parser"); + return <>; + }, + { + [$TOKEN]: true, + ...meta, + props + } + ); + }; + } -export function tokenize( - callback: (props: T) => any, - meta?: { [key: string]: any }, - component?: (props: T) => JSXElement -): (props: T) => JSXElement { - return (props: any) => { - return Object.assign( - () => { - if (!component) - console.info( - "only Tokens tokenized with a component-parameter can be renderered outside a Parser" - ); - return <>} />; - }, - { - props, - [$TOKEN]: true, - meta, - callback: () => callback(props) - } + function childrenTokens(children: () => JSXElement | JSXElement[] | T | T[]) { + return createMemo(() => + children + ? ([] as any[]) + .concat(children()) + .filter(child => child && $TOKEN in child) + .map((a: T) => a as T) + : [] ); - }; -} + } -export function childrenTokens(children: () => JSXElement | JSXElement[] | T | T[]) { - return createMemo(() => - children - ? ([] as any[]) - .concat(children()) - .filter(child => child && $TOKEN in child) - .map((a: T) => a as T) - : [] - ); + return { tokenize, childrenTokens, $TOKEN }; } +type Meta = { [key: string]: any; id: string }; + +export type Token = TMeta & { + props: TProps; +}; From bd305e88cbb46d319980bce075a5fe8616c5f831 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 17:47:15 +0100 Subject: [PATCH 04/28] tokenize-callback, remove Token-type helper replace the meta-object with a callback through which you pass the component-properties. remove Token-type helper to not prescribe to a way the tokens should be typed --- packages/parser/dev/index.tsx | 25 +++++++++++++++++-------- packages/parser/package.json | 1 - packages/parser/src/index.tsx | 15 ++++----------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx index 94b40d7be..47cd03537 100644 --- a/packages/parser/dev/index.tsx +++ b/packages/parser/dev/index.tsx @@ -1,7 +1,7 @@ import { Component, createMemo, For, JSXElement } from "solid-js"; import { render } from "solid-js/web"; import "uno.css"; -import { Token, createParser } from "../src"; +import { createParser } from "../src"; const { tokenize, childrenTokens } = createParser("calculator"); @@ -44,26 +44,35 @@ const Calculator = (props: { type MetaValue = { id: "Value"; } & Meta; -type TokenValue = Token; +type TokenValue = MetaValue & { props: Props }; -const Value = tokenize({ id: "Value", callback: props => <>{props.value} }); +const Value = tokenize(props => ({ + props, + id: "Value", + callback: props => <>{props.value} +})); type MetaAdd = { id: "Add"; } & Meta; -type TokenAdd = Token; +type TokenAdd = MetaAdd & { props: Props }; -const Add = tokenize({ id: "Add", callback: (props: Props) => <> + {props.value} }); +const Add = tokenize(props => ({ + props, + id: "Add", + callback: (props: Props) => <> + {props.value} +})); type MetaSubtract = { id: "Subtract"; } & Meta; -type TokenSubtract = Token; +type TokenSubtract = MetaSubtract & { props: Props }; -const Subtract = tokenize({ +const Subtract = tokenize(props => ({ + props, id: "Subtract", callback: (props: Props) => <> - {props.value} -}); +})); const App: Component = () => { return ( diff --git a/packages/parser/package.json b/packages/parser/package.json index 955a3cf88..fc0425b79 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -17,7 +17,6 @@ "name": "parser", "stage": 0, "list": [ - "Token", "createParser" ], "category": "Utilities" diff --git a/packages/parser/src/index.tsx b/packages/parser/src/index.tsx index 7229997f0..b70d85706 100644 --- a/packages/parser/src/index.tsx +++ b/packages/parser/src/index.tsx @@ -4,10 +4,9 @@ export function createParser(id?: string) { const $TOKEN = Symbol(id || "solid-parser"); function tokenize< - TToken extends Meta & { - props: unknown; - } - >(meta: Omit): (props: TToken["props"]) => JSXElement { + TProps extends { [key: string]: any }, + TToken extends { [key: string]: any } & { id: string } + >(tokenProperties: (props: TProps) => TToken): (props: TToken["props"]) => JSXElement { return (props: any) => { return Object.assign( () => { @@ -16,8 +15,7 @@ export function createParser(id?: string) { }, { [$TOKEN]: true, - ...meta, - props + ...tokenProperties(props) } ); }; @@ -36,8 +34,3 @@ export function createParser(id?: string) { return { tokenize, childrenTokens, $TOKEN }; } -type Meta = { [key: string]: any; id: string }; - -export type Token = TMeta & { - props: TProps; -}; From a5434d329b575a879d02aec21239a0414ecd0e14 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 18:24:09 +0100 Subject: [PATCH 05/28] warning behind DEV, id to warning, optional 2nd param for component-callback, tsx to ts --- packages/parser/dev/index.tsx | 30 ++++++++++++--------- packages/parser/src/{index.tsx => index.ts} | 22 +++++++++------ 2 files changed, 32 insertions(+), 20 deletions(-) rename packages/parser/src/{index.tsx => index.ts} (56%) diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx index 47cd03537..bd632dddd 100644 --- a/packages/parser/dev/index.tsx +++ b/packages/parser/dev/index.tsx @@ -21,7 +21,7 @@ const Calculator = (props: { const calculation = () => { let result = 0; tokens().forEach(token => { - console.log("token is ", token); + console.info("token is ", token); if (token.id === "Value") { result = token.props.value; } else if (token.id === "Add") { @@ -29,7 +29,7 @@ const Calculator = (props: { } else if (token.id === "Subtract") { result -= token.props.value; } - console.log("result is", result); + console.info("result is", result); }); return result; }; @@ -46,11 +46,14 @@ type MetaValue = { } & Meta; type TokenValue = MetaValue & { props: Props }; -const Value = tokenize(props => ({ - props, - id: "Value", - callback: props => <>{props.value} -})); +const Value = tokenize( + props => ({ + props, + id: "Value", + callback: props => <>{props.value} + }), + props => <>value outside of a calculator: {props.value} +); type MetaAdd = { id: "Add"; @@ -76,11 +79,14 @@ const Subtract = tokenize(props => ({ const App: Component = () => { return ( - - - - - + <> + + + + + + + ); }; diff --git a/packages/parser/src/index.tsx b/packages/parser/src/index.ts similarity index 56% rename from packages/parser/src/index.tsx rename to packages/parser/src/index.ts index b70d85706..9d54294d3 100644 --- a/packages/parser/src/index.tsx +++ b/packages/parser/src/index.ts @@ -1,18 +1,24 @@ import { JSXElement, createMemo } from "solid-js"; -export function createParser(id?: string) { - const $TOKEN = Symbol(id || "solid-parser"); +export function createParser(id: string = "solid-parser") { + const $TOKEN = Symbol(id); function tokenize< TProps extends { [key: string]: any }, TToken extends { [key: string]: any } & { id: string } - >(tokenProperties: (props: TProps) => TToken): (props: TToken["props"]) => JSXElement { - return (props: any) => { + >( + tokenProperties: (props: TProps) => TToken, + component?: (props: TProps) => JSXElement + ): (props: TToken["props"]) => JSXElement { + return (props: TProps) => { return Object.assign( - () => { - console.info("tokens can only be rendered inside a Parser"); - return <>; - }, + component + ? () => component(props) + : () => { + process.env.DEV && + console.info(`tokens can only be rendered inside a Parser with id '${id}'`); + return ""; + }, { [$TOKEN]: true, ...tokenProperties(props) From 8e40c76f5dda8cc281cf9463b21b85e677c23838 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 18:27:41 +0100 Subject: [PATCH 06/28] parser -> jsx-parser --- packages/parser/dev/index.tsx | 4 ++-- packages/parser/package.json | 8 ++++---- packages/parser/src/index.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx index bd632dddd..8856a6882 100644 --- a/packages/parser/dev/index.tsx +++ b/packages/parser/dev/index.tsx @@ -1,9 +1,9 @@ import { Component, createMemo, For, JSXElement } from "solid-js"; import { render } from "solid-js/web"; import "uno.css"; -import { createParser } from "../src"; +import { createJSXParser } from "../src"; -const { tokenize, childrenTokens } = createParser("calculator"); +const { tokenize, childrenTokens } = createJSXParser("calculator"); type Meta = { callback: (props: Props) => JSXElement }; type Props = { diff --git a/packages/parser/package.json b/packages/parser/package.json index fc0425b79..5a9bb96a5 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,5 +1,5 @@ { - "name": "@solid-primitives/parser", + "name": "@solid-primitives/jsx-parser", "version": "0.0.101", "description": "A primitive to tokenize your solid-components to enable custom parsing.", "author": "Vincent Van Dijck ", @@ -14,17 +14,17 @@ "url": "https://github.com/solidjs-community/solid-primitives/issues" }, "primitive": { - "name": "parser", + "name": "jsx-parser", "stage": 0, "list": [ - "createParser" + "createJSXParser" ], "category": "Utilities" }, "keywords": [ "solid", "primitives", - "parser" + "jsx-parser" ], "private": false, "sideEffects": false, diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index 9d54294d3..b437b6895 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -1,6 +1,6 @@ import { JSXElement, createMemo } from "solid-js"; -export function createParser(id: string = "solid-parser") { +export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); function tokenize< From 5e555e1525c95f9a67ff0354f1e38768db736d77 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 18:28:36 +0100 Subject: [PATCH 07/28] tokenize -> createToken --- packages/parser/dev/index.tsx | 8 ++++---- packages/parser/src/index.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/parser/dev/index.tsx b/packages/parser/dev/index.tsx index 8856a6882..a1eca9e67 100644 --- a/packages/parser/dev/index.tsx +++ b/packages/parser/dev/index.tsx @@ -3,7 +3,7 @@ import { render } from "solid-js/web"; import "uno.css"; import { createJSXParser } from "../src"; -const { tokenize, childrenTokens } = createJSXParser("calculator"); +const { createToken, childrenTokens } = createJSXParser("calculator"); type Meta = { callback: (props: Props) => JSXElement }; type Props = { @@ -46,7 +46,7 @@ type MetaValue = { } & Meta; type TokenValue = MetaValue & { props: Props }; -const Value = tokenize( +const Value = createToken( props => ({ props, id: "Value", @@ -60,7 +60,7 @@ type MetaAdd = { } & Meta; type TokenAdd = MetaAdd & { props: Props }; -const Add = tokenize(props => ({ +const Add = createToken(props => ({ props, id: "Add", callback: (props: Props) => <> + {props.value} @@ -71,7 +71,7 @@ type MetaSubtract = { } & Meta; type TokenSubtract = MetaSubtract & { props: Props }; -const Subtract = tokenize(props => ({ +const Subtract = createToken(props => ({ props, id: "Subtract", callback: (props: Props) => <> - {props.value} diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index b437b6895..2215fd681 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -3,7 +3,7 @@ import { JSXElement, createMemo } from "solid-js"; export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); - function tokenize< + function createToken< TProps extends { [key: string]: any }, TToken extends { [key: string]: any } & { id: string } >( @@ -38,5 +38,5 @@ export function createJSXParser(id: string = "solid-parser") { ); } - return { tokenize, childrenTokens, $TOKEN }; + return { createToken, childrenTokens, $TOKEN }; } From 0f3f90c06746d8633cd18ecf6197a51aedc8542a Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 19:07:53 +0100 Subject: [PATCH 08/28] add --dev tag to package.json --- packages/parser/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parser/package.json b/packages/parser/package.json index 5a9bb96a5..66535af93 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -61,7 +61,7 @@ }, "scripts": { "dev": "vite serve dev", - "build": "jiti ../../scripts/build.ts --ssr", + "build": "jiti ../../scripts/build.ts --ssr --dev", "test": "vitest -c ../../configs/vitest.config.ts", "test:ssr": "pnpm run test --mode ssr" }, From 9d9a42051bdf084d0408c6ef32341fe441661e24 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 19:09:29 +0100 Subject: [PATCH 09/28] rename folder parser -> jsx-parser --- packages/{parser => jsx-parser}/LICENSE | 0 packages/{parser => jsx-parser}/README.md | 0 packages/{parser => jsx-parser}/dev/index.html | 0 packages/{parser => jsx-parser}/dev/index.tsx | 0 packages/{parser => jsx-parser}/dev/vite.config.ts | 0 packages/{parser => jsx-parser}/package.json | 0 packages/{parser => jsx-parser}/src/index.ts | 0 packages/{parser => jsx-parser}/test/index.test.ts | 0 packages/{parser => jsx-parser}/test/server.test.ts | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename packages/{parser => jsx-parser}/LICENSE (100%) rename packages/{parser => jsx-parser}/README.md (100%) rename packages/{parser => jsx-parser}/dev/index.html (100%) rename packages/{parser => jsx-parser}/dev/index.tsx (100%) rename packages/{parser => jsx-parser}/dev/vite.config.ts (100%) rename packages/{parser => jsx-parser}/package.json (100%) rename packages/{parser => jsx-parser}/src/index.ts (100%) rename packages/{parser => jsx-parser}/test/index.test.ts (100%) rename packages/{parser => jsx-parser}/test/server.test.ts (100%) diff --git a/packages/parser/LICENSE b/packages/jsx-parser/LICENSE similarity index 100% rename from packages/parser/LICENSE rename to packages/jsx-parser/LICENSE diff --git a/packages/parser/README.md b/packages/jsx-parser/README.md similarity index 100% rename from packages/parser/README.md rename to packages/jsx-parser/README.md diff --git a/packages/parser/dev/index.html b/packages/jsx-parser/dev/index.html similarity index 100% rename from packages/parser/dev/index.html rename to packages/jsx-parser/dev/index.html diff --git a/packages/parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx similarity index 100% rename from packages/parser/dev/index.tsx rename to packages/jsx-parser/dev/index.tsx diff --git a/packages/parser/dev/vite.config.ts b/packages/jsx-parser/dev/vite.config.ts similarity index 100% rename from packages/parser/dev/vite.config.ts rename to packages/jsx-parser/dev/vite.config.ts diff --git a/packages/parser/package.json b/packages/jsx-parser/package.json similarity index 100% rename from packages/parser/package.json rename to packages/jsx-parser/package.json diff --git a/packages/parser/src/index.ts b/packages/jsx-parser/src/index.ts similarity index 100% rename from packages/parser/src/index.ts rename to packages/jsx-parser/src/index.ts diff --git a/packages/parser/test/index.test.ts b/packages/jsx-parser/test/index.test.ts similarity index 100% rename from packages/parser/test/index.test.ts rename to packages/jsx-parser/test/index.test.ts diff --git a/packages/parser/test/server.test.ts b/packages/jsx-parser/test/server.test.ts similarity index 100% rename from packages/parser/test/server.test.ts rename to packages/jsx-parser/test/server.test.ts From c4ffe61595dc252f86330488eb82dc5fe396a00c Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 20:30:08 +0100 Subject: [PATCH 10/28] remove useless map in childrenTokens --- packages/jsx-parser/src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 2215fd681..91c147f48 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -30,10 +30,7 @@ export function createJSXParser(id: string = "solid-parser") { function childrenTokens(children: () => JSXElement | JSXElement[] | T | T[]) { return createMemo(() => children - ? ([] as any[]) - .concat(children()) - .filter(child => child && $TOKEN in child) - .map((a: T) => a as T) + ? (([] as any[]).concat(children()).filter(child => child && $TOKEN in child) as T[]) : [] ); } From e35fac886b7242885769b1a70af0e1b9b145c322 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 21:23:21 +0100 Subject: [PATCH 11/28] add generic accepted TokenTypes to createJSXParser --- packages/jsx-parser/dev/index.tsx | 12 +++++------- packages/jsx-parser/src/index.ts | 13 +++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index a1eca9e67..36a355391 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -3,8 +3,6 @@ import { render } from "solid-js/web"; import "uno.css"; import { createJSXParser } from "../src"; -const { createToken, childrenTokens } = createJSXParser("calculator"); - type Meta = { callback: (props: Props) => JSXElement }; type Props = { value: number; @@ -13,9 +11,9 @@ type Props = { type CustomToken = TokenValue | TokenAdd | TokenSubtract; -const Calculator = (props: { - children: JSXElement | JSXElement[] | CustomToken | CustomToken[]; -}) => { +const { createToken, childrenTokens } = createJSXParser("calculator"); + +const Calculator = (props: { children: JSXElement | JSXElement[] }) => { const tokens = childrenTokens(() => props.children); const calculation = () => { @@ -46,8 +44,8 @@ type MetaValue = { } & Meta; type TokenValue = MetaValue & { props: Props }; -const Value = createToken( - props => ({ +const Value = createToken( + (props: Props) => ({ props, id: "Value", callback: props => <>{props.value} diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 91c147f48..bcc21d641 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,15 +1,12 @@ import { JSXElement, createMemo } from "solid-js"; -export function createJSXParser(id: string = "solid-parser") { +export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); - function createToken< - TProps extends { [key: string]: any }, - TToken extends { [key: string]: any } & { id: string } - >( + function createToken( tokenProperties: (props: TProps) => TToken, component?: (props: TProps) => JSXElement - ): (props: TToken["props"]) => JSXElement { + ): (props: TProps) => JSXElement { return (props: TProps) => { return Object.assign( component @@ -27,10 +24,10 @@ export function createJSXParser(id: string = "solid-parser") { }; } - function childrenTokens(children: () => JSXElement | JSXElement[] | T | T[]) { + function childrenTokens(children: () => JSXElement | JSXElement[]): () => TTokens[] { return createMemo(() => children - ? (([] as any[]).concat(children()).filter(child => child && $TOKEN in child) as T[]) + ? (([] as any[]).concat(children()).filter(child => child && $TOKEN in child) as TTokens[]) : [] ); } From 513c3d46c4b77f0ddf43301bef7afa7cf0672317 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 4 Jan 2023 21:56:07 +0100 Subject: [PATCH 12/28] childrenTokens: remove typecast, change returnType set returnType to Accessor instead of () => TTokens --- packages/jsx-parser/src/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index bcc21d641..9138c7ca8 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,4 +1,4 @@ -import { JSXElement, createMemo } from "solid-js"; +import { JSXElement, createMemo, Accessor } from "solid-js"; export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); @@ -24,11 +24,9 @@ export function createJSXParser(id: string = "solid-parser") { }; } - function childrenTokens(children: () => JSXElement | JSXElement[]): () => TTokens[] { + function childrenTokens(children: () => JSXElement | JSXElement[]): Accessor { return createMemo(() => - children - ? (([] as any[]).concat(children()).filter(child => child && $TOKEN in child) as TTokens[]) - : [] + children ? ([] as any[]).concat(children()).filter(child => child && $TOKEN in child) : [] ); } From 8a1927be9f9cfeb1ab9d21474cd5fd7feb6b5793 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Thu, 5 Jan 2023 00:42:54 +0100 Subject: [PATCH 13/28] childrenTokens: memo callback and resolveChild --- packages/jsx-parser/src/index.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 9138c7ca8..340fb579a 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -24,9 +24,20 @@ export function createJSXParser(id: string = "solid-parser") { }; } - function childrenTokens(children: () => JSXElement | JSXElement[]): Accessor { + function childrenTokens(fn: Accessor): Accessor { + const children = createMemo(fn); + const resolveChild = (child: any) => { + while (true) { + if (typeof child !== "function") return child; + if ($TOKEN in child) return child; + child = child(); + } + }; return createMemo(() => - children ? ([] as any[]).concat(children()).filter(child => child && $TOKEN in child) : [] + ([] as any[]) + .concat(children()) + .map(resolveChild) + .filter(child => child && $TOKEN in child) ); } From 52e6468e762c8f155bb5681dca7bf08e3cbca05b Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Thu, 5 Jan 2023 17:07:37 +0100 Subject: [PATCH 14/28] childrenTokens: support arrays, For, Index --- packages/jsx-parser/src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 340fb579a..db5e33e94 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -26,8 +26,9 @@ export function createJSXParser(id: string = "solid-parser") { function childrenTokens(fn: Accessor): Accessor { const children = createMemo(fn); - const resolveChild = (child: any) => { + const resolveChild = (child: any): any => { while (true) { + if (Array.isArray(child)) return child.map(resolveChild).flat(); if (typeof child !== "function") return child; if ($TOKEN in child) return child; child = child(); @@ -37,6 +38,7 @@ export function createJSXParser(id: string = "solid-parser") { ([] as any[]) .concat(children()) .map(resolveChild) + .flat() .filter(child => child && $TOKEN in child) ); } From bd1cfc8898d351fe9b1b367e6054eca744e32f44 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Fri, 6 Jan 2023 14:28:42 +0100 Subject: [PATCH 15/28] createToken: pass owner to token-callback --- packages/jsx-parser/src/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index db5e33e94..a9456c8d3 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,13 +1,14 @@ -import { JSXElement, createMemo, Accessor } from "solid-js"; +import { Accessor, createMemo, getOwner, JSXElement, Owner } from "solid-js"; -export function createJSXParser(id: string = "solid-parser") { +export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); function createToken( - tokenProperties: (props: TProps) => TToken, + tokenProperties: (props: TProps, owner: Owner | null) => TToken, component?: (props: TProps) => JSXElement ): (props: TProps) => JSXElement { return (props: TProps) => { + const owner = getOwner(); return Object.assign( component ? () => component(props) @@ -18,13 +19,13 @@ export function createJSXParser(id: string = "solid-parser") { }, { [$TOKEN]: true, - ...tokenProperties(props) + ...tokenProperties(props, owner) } ); }; } - function childrenTokens(fn: Accessor): Accessor { + function childrenTokens(fn: Accessor): Accessor { const children = createMemo(fn); const resolveChild = (child: any): any => { while (true) { From b4857d2d695e25478fa4f10789e6da7b7599b950 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Fri, 6 Jan 2023 17:30:24 +0100 Subject: [PATCH 16/28] remove owner from tokenProperties --- packages/jsx-parser/src/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index a9456c8d3..71a1235c4 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,14 +1,13 @@ -import { Accessor, createMemo, getOwner, JSXElement, Owner } from "solid-js"; +import { Accessor, createMemo, JSXElement } from "solid-js"; export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); function createToken( - tokenProperties: (props: TProps, owner: Owner | null) => TToken, + tokenProperties: (props: TProps) => TToken, component?: (props: TProps) => JSXElement ): (props: TProps) => JSXElement { return (props: TProps) => { - const owner = getOwner(); return Object.assign( component ? () => component(props) @@ -19,13 +18,13 @@ export function createJSXParser(id: string = "solid-parser") }, { [$TOKEN]: true, - ...tokenProperties(props, owner) + ...tokenProperties(props) } ); }; } - function childrenTokens(fn: Accessor): Accessor { + function childrenTokens(fn: Accessor): Accessor { const children = createMemo(fn); const resolveChild = (child: any): any => { while (true) { From 0bbcd9bec6cf7be8748f89de5f8edf7a57f1a665 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Fri, 6 Jan 2023 17:48:46 +0100 Subject: [PATCH 17/28] createToken: cache to prevent token remounts --- packages/jsx-parser/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 71a1235c4..64f9bc901 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -2,13 +2,15 @@ import { Accessor, createMemo, JSXElement } from "solid-js"; export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); + const tokenCache = new WeakMap(); function createToken( tokenProperties: (props: TProps) => TToken, component?: (props: TProps) => JSXElement ): (props: TProps) => JSXElement { return (props: TProps) => { - return Object.assign( + if (tokenCache.has(tokenProperties)) return tokenCache.get(tokenProperties); + const token = Object.assign( component ? () => component(props) : () => { @@ -21,6 +23,8 @@ export function createJSXParser(id: string = "solid-parser") ...tokenProperties(props) } ); + tokenCache.set(tokenProperties, token); + return token; }; } From 163903f3e64c5f3f615d100016d345b0a5ac3847 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Fri, 6 Jan 2023 17:49:31 +0100 Subject: [PATCH 18/28] createToken: tokenProperties rename tokenCallback --- packages/jsx-parser/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 64f9bc901..81417cb67 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -5,11 +5,11 @@ export function createJSXParser(id: string = "solid-parser") const tokenCache = new WeakMap(); function createToken( - tokenProperties: (props: TProps) => TToken, + tokenCallback: (props: TProps) => TToken, component?: (props: TProps) => JSXElement ): (props: TProps) => JSXElement { return (props: TProps) => { - if (tokenCache.has(tokenProperties)) return tokenCache.get(tokenProperties); + if (tokenCache.has(tokenCallback)) return tokenCache.get(tokenCallback); const token = Object.assign( component ? () => component(props) @@ -20,10 +20,10 @@ export function createJSXParser(id: string = "solid-parser") }, { [$TOKEN]: true, - ...tokenProperties(props) + ...tokenCallback(props) } ); - tokenCache.set(tokenProperties, token); + tokenCache.set(tokenCallback, token); return token; }; } From 11148d40be132f68e88177be3d004eb3a2b01e0d Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Fri, 6 Jan 2023 23:06:24 +0100 Subject: [PATCH 19/28] createToken: remove cache --- packages/jsx-parser/src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 81417cb67..d6c8bac2e 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -2,14 +2,12 @@ import { Accessor, createMemo, JSXElement } from "solid-js"; export function createJSXParser(id: string = "solid-parser") { const $TOKEN = Symbol(id); - const tokenCache = new WeakMap(); function createToken( tokenCallback: (props: TProps) => TToken, component?: (props: TProps) => JSXElement ): (props: TProps) => JSXElement { return (props: TProps) => { - if (tokenCache.has(tokenCallback)) return tokenCache.get(tokenCallback); const token = Object.assign( component ? () => component(props) @@ -23,7 +21,6 @@ export function createJSXParser(id: string = "solid-parser") ...tokenCallback(props) } ); - tokenCache.set(tokenCallback, token); return token; }; } From 2176c489ff0b6ad6929c7f6e842eae88df6518a5 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Sat, 7 Jan 2023 15:48:30 +0100 Subject: [PATCH 20/28] isToken: function to check if value is a token --- packages/jsx-parser/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index d6c8bac2e..17b0412ba 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -44,5 +44,9 @@ export function createJSXParser(id: string = "solid-parser") ); } - return { createToken, childrenTokens, $TOKEN }; + function isToken(value: any) { + return typeof value === "function" && $TOKEN in value && (value as TTokens); + } + + return { createToken, childrenTokens, isToken, $TOKEN }; } From 36675667fb00886edddd4b54b6926c876c931932 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Mon, 23 Jan 2023 17:30:11 +0100 Subject: [PATCH 21/28] update example, simplify types --- packages/jsx-parser/dev/index.tsx | 62 +++++++++++++++++-------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index 36a355391..8a77096a1 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -3,7 +3,6 @@ import { render } from "solid-js/web"; import "uno.css"; import { createJSXParser } from "../src"; -type Meta = { callback: (props: Props) => JSXElement }; type Props = { value: number; children?: JSXElement | JSXElement[]; @@ -34,57 +33,66 @@ const Calculator = (props: { children: JSXElement | JSXElement[] }) => { return (
- {token => token.callback(token.props)} = {calculation()} + {props.children} = {calculation()}
); }; -type MetaValue = { +type TokenValue = { id: "Value"; -} & Meta; -type TokenValue = MetaValue & { props: Props }; + props: Props; +}; const Value = createToken( (props: Props) => ({ props, - id: "Value", - callback: props => <>{props.value} + id: "Value" }), - props => <>value outside of a calculator: {props.value} + props => <>{props.value} ); -type MetaAdd = { +type TokenAdd = { id: "Add"; -} & Meta; -type TokenAdd = MetaAdd & { props: Props }; + props: Props; +}; -const Add = createToken(props => ({ - props, - id: "Add", - callback: (props: Props) => <> + {props.value} -})); +const Add = createToken( + props => ({ + props, + id: "Add" + }), + props => <> + {props.value} +); -type MetaSubtract = { +type TokenSubtract = { id: "Subtract"; -} & Meta; -type TokenSubtract = MetaSubtract & { props: Props }; + props: Props; +}; -const Subtract = createToken(props => ({ - props, - id: "Subtract", - callback: (props: Props) => <> - {props.value} -})); +const Subtract = createToken( + props => ({ + props, + id: "Subtract" + }), + props => <> - {props.value} +); const App: Component = () => { return ( - <> - +
- +
); }; From 4203f05f42d0034136146bf0d86aa6626850393c Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Mon, 23 Jan 2023 17:30:48 +0100 Subject: [PATCH 22/28] write documentation --- packages/jsx-parser/README.md | 113 +++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/packages/jsx-parser/README.md b/packages/jsx-parser/README.md index 38b12282c..81f8e889a 100644 --- a/packages/jsx-parser/README.md +++ b/packages/jsx-parser/README.md @@ -4,19 +4,126 @@ # @solid-primitives/parser +A primitive to extend the types of values JSX can return. These JSX-elements are named `tokens`. + +- [`createJSXParser`](#createJSXParser) — Provides the tools to create and identify `tokens`: `createToken`, `childrenTokens`, `isToken` and `$TOKEN`. +- [`createToken`](#createToken) — Instantiates a `token` associated with the corresponding jsx-parser. +- [`childrenTokens`](#childrenTokens) — A function similar to Solid's `children()`, but that will only return valid `tokens` created by the corresponding jsx-parser's `createToken` +- [`isToken`](#isToken) — A function to validate if an element is a `token` created by the corresponding jsx-parser's `createToken` +- [`$TOKEN`](#$TOKEN) — The symbol unique to the corresponding jsx-parser + ## Installation ```bash -npm install @solid-primitives/parser +npm install @solid-primitives/jsx-parser # or yarn add @solid-primitives/parser # or pnpm add @solid-primitives/parser ``` -## How to use it +## `createJSXParser` + +Provides the tools to create and identify `tokens`. + +### How to use it + +`createJSXParser` takes an optional id as argument, and returns the following functions `createToken`, `childrenTokens`, `isToken` and the symbol associated with the jsx-parser `$TOKEN`. + +It also takes as a generic the union of accepted token-types. + +```tsx +import { createJSXParser } from "@solid-primitives/jsx-parser"; + +type UnionOfAcceptedTokens = Token1 | Token2 | ... + +const {createToken, childrenTokens, isToken, $TOKEN} = createJSXParser('parser-example'); +``` + +## `createToken` + +A function to create a `token` associated with the corresponding jsx-parser. + +### How to use it + +`createToken` takes two callback-arguments: the first callback returns the token, an optional second callback returns a JSXElement. This second callback is used when the token is being rendered by Solid. If the second callback is not present, the following error will be shown instead: `tokens can only be rendered inside a Parser with id ...` + +It takes as generic two types: the props of the associated JSX-element and the return-value of the token. + +```tsx +type Props = { + id: string; +}; + +type Token = { + props: Props; + value: number; +}; + +const TokenExample = createToken( + props => { + const value = Math.random(); + return { + props, + value + }; + }, + props => { + return {props.id}; + } +); +``` + +This token can then be used as a JSX-element inside your solid-code: -## TODO +```tsx +const App = () => { + return ; +}; +``` + +TokenExample is typed as a JSXElement, this is so TokenExample can be used in JSX without causing type-errors. + +## `childrenTokens` + +A function similar to Solid's [`children()`](https://www.solidjs.com/docs/latest#children), but that will only return valid `tokens` created by the corresponding jsx-parser's `createToken` + +### How to use it + +`childrenTokens` takes a callback returning `props.children`, and will return all tokens associated with the corresponding jsx-parser. Just like Solid's `children`, `childrenTokens` will resolve the tokens: multiple `childrenTokens` of the same `props.children` will execute the token-callback multiple times! + +```tsx +const tokens = childrenTokens(() => props.children); +``` + +## `isToken` + +A function to validate if a value is a token created by the corresponding jsx-parser, by checking if the value contains the `$TOKEN`-symbol. + +### How to use it + +`isToken` takes a value, often this would be a JSXElement. The function returns `false` in case the value is not a token created by the corresponding jsx-parser. In case the value is a token `isToken` returns the value cast to a `token`. + +```tsx +const value = props.children[0]; // value is typed as a JSXElement +const token = isToken(value); +if (!token) return; +token; // token is typed as UnionOfAcceptedTokens +``` + +## `$TOKEN` + +The symbol which is attached to all tokens of the corresponding jsx-parser. This is internally used in `childrenTokens` and `isToken` to validate if a value is a token. + +### How to use it + +`$TOKEN` can be used for validation. + +```tsx +const value = props.children[0]; +if (!($TOKEN in value)) return; +const token = value as UnionOfAcceptedTokens; +``` ## Demo From b9a61330f4ad39ac0dfd76d05ab3eaac61401c4d Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Mon, 23 Jan 2023 18:56:52 +0100 Subject: [PATCH 23/28] update readme solid-primitives --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f45f97fe7..d8933a4e8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![pnpm](https://img.shields.io/badge/maintained%20with-pnpm-cc00ff.svg?style=for-the-badge&logo=pnpm)](https://pnpm.io/) [![turborepo](https://img.shields.io/badge/built%20with-turborepo-cc00ff.svg?style=for-the-badge&logo=turborepo)](https://turborepo.org/) -[![combined-downloads](https://img.shields.io/endpoint?style=for-the-badge&url=https://combined-npm-downloads.deno.dev/@solid-primitives/analytics,@solid-primitives/broadcast-channel,@solid-primitives/context,@solid-primitives/devices,@solid-primitives/event-dispatcher,@solid-primitives/event-props,@solid-primitives/fetch,@solid-primitives/fullscreen,@solid-primitives/gestures,@solid-primitives/i18n,@solid-primitives/idle,@solid-primitives/input-mask,@solid-primitives/keyed,@solid-primitives/permission,@solid-primitives/platform,@solid-primitives/scheduled,@solid-primitives/script-loader,@solid-primitives/selection,@solid-primitives/share,@solid-primitives/storage,@solid-primitives/timer,@solid-primitives/tween,@solid-primitives/utils,@solid-primitives/websocket,@solid-primitives/workers)](https://dash.deno.com/playground/combined-npm-downloads) +[![combined-downloads](https://img.shields.io/endpoint?style=for-the-badge&url=https://combined-npm-downloads.deno.dev/@solid-primitives/analytics,@solid-primitives/broadcast-channel,@solid-primitives/context,@solid-primitives/devices,@solid-primitives/event-dispatcher,@solid-primitives/event-props,@solid-primitives/fetch,@solid-primitives/fullscreen,@solid-primitives/gestures,@solid-primitives/i18n,@solid-primitives/idle,@solid-primitives/input-mask,@solid-primitives/jsx-parser,@solid-primitives/keyed,@solid-primitives/permission,@solid-primitives/platform,@solid-primitives/scheduled,@solid-primitives/script-loader,@solid-primitives/selection,@solid-primitives/share,@solid-primitives/storage,@solid-primitives/timer,@solid-primitives/tween,@solid-primitives/utils,@solid-primitives/websocket,@solid-primitives/workers)](https://dash.deno.com/playground/combined-npm-downloads) A project that strives to develop high-quality, community contributed Solid primitives. All utilities are well tested and continuously maintained. Every contribution to the repository is checked for quality and maintained to the highest degree of excellence. The ultimate goal is to extend Solid's primary and secondary primitives with a set of tertiary primitives. @@ -66,6 +66,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to |[event-dispatcher](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-dispatcher#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createEventDispatcher](https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-dispatcher#createeventdispatcher)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/event-dispatcher?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/event-dispatcher)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/event-dispatcher?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/event-dispatcher)| |[i18n](https://github.com/solidjs-community/solid-primitives/tree/main/packages/i18n#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createI18nContext](https://github.com/solidjs-community/solid-primitives/tree/main/packages/i18n#createi18ncontext)
[useI18n](https://github.com/solidjs-community/solid-primitives/tree/main/packages/i18n#usei18n)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/i18n?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/i18n)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/i18n?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/i18n)| |[immutable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/immutable#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-2.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[List of functions](https://github.com/solidjs-community/solid-primitives/tree/main/packages/immutable#list-of-functions)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/immutable?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/immutable)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/immutable?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/immutable)| +|[jsx-parser](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-parser#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createJSXParser](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-parser#createjsxparser)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/jsx-parser?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/jsx-parser)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/jsx-parser?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/jsx-parser)| |[map](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-2.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[ReactiveMap](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#reactivemap)
[ReactiveWeakMap](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#reactiveweakmap)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/map?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/map)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/map?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/map)| |[pagination](https://github.com/solidjs-community/solid-primitives/tree/main/packages/pagination#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[createPagination](https://github.com/solidjs-community/solid-primitives/tree/main/packages/pagination#createpagination)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/pagination?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/pagination)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/pagination?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/pagination)| |[platform](https://github.com/solidjs-community/solid-primitives/tree/main/packages/platform#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives#contribution-process)|[List of variables](https://github.com/solidjs-community/solid-primitives/tree/main/packages/platform#list-of-variables)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/platform?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/platform)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/platform?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/platform)| From 2db7a2f9881090c2865ee4ed07704c4789b73659 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 26 Jan 2023 18:58:29 +0100 Subject: [PATCH 24/28] Update lockfile --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0be5e2106..9d6172c8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,6 +314,12 @@ importers: devDependencies: '@solid-primitives/range': link:../range + packages/jsx-parser: + specifiers: + solid-js: ^1.6.0 + dependencies: + solid-js: 1.6.9 + packages/keyboard: specifiers: '@solid-primitives/event-listener': workspace:^2.2.5 From a0f8f3bda7cc8a36c20e4b36a483009ebb8307c8 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 26 Jan 2023 19:25:37 +0100 Subject: [PATCH 25/28] Cleanup jsx-parser --- packages/jsx-parser/dev/index.tsx | 6 +-- packages/jsx-parser/package.json | 38 ++++++--------- packages/jsx-parser/src/index.ts | 62 +++++++++++++------------ packages/jsx-parser/test/index.test.ts | 0 packages/jsx-parser/test/server.test.ts | 0 5 files changed, 50 insertions(+), 56 deletions(-) delete mode 100644 packages/jsx-parser/test/index.test.ts delete mode 100644 packages/jsx-parser/test/server.test.ts diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index 8a77096a1..67846f38d 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -1,18 +1,18 @@ -import { Component, createMemo, For, JSXElement } from "solid-js"; +import { Component, JSX, ParentComponent } from "solid-js"; import { render } from "solid-js/web"; import "uno.css"; import { createJSXParser } from "../src"; type Props = { value: number; - children?: JSXElement | JSXElement[]; + children?: JSX.Element | JSX.Element[]; }; type CustomToken = TokenValue | TokenAdd | TokenSubtract; const { createToken, childrenTokens } = createJSXParser("calculator"); -const Calculator = (props: { children: JSXElement | JSXElement[] }) => { +const Calculator: ParentComponent = props => { const tokens = childrenTokens(() => props.children); const calculation = () => { diff --git a/packages/jsx-parser/package.json b/packages/jsx-parser/package.json index 66535af93..e8d02a8ab 100644 --- a/packages/jsx-parser/package.json +++ b/packages/jsx-parser/package.json @@ -1,6 +1,6 @@ { "name": "@solid-primitives/jsx-parser", - "version": "0.0.101", + "version": "0.0.1", "description": "A primitive to tokenize your solid-components to enable custom parsing.", "author": "Vincent Van Dijck ", "contributors": [], @@ -32,36 +32,28 @@ "dist" ], "type": "module", - "main": "./dist/server.cjs", - "module": "./dist/server.js", - "browser": { - "./dist/server.cjs": "./dist/index.cjs", - "./dist/server.js": "./dist/index.js" - }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", "types": "./dist/index.d.ts", + "browser": {}, "exports": { - "worker": { - "import": "./dist/server.js", - "require": "./dist/server.cjs" - }, - "browser": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "deno": { - "import": "./dist/server.js", - "require": "./dist/server.cjs" + "development": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/dev.js" + }, + "require": "./dist/dev.cjs" }, - "node": { - "import": "./dist/server.js", - "require": "./dist/server.cjs" + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" }, - "import": "./dist/index.js", "require": "./dist/index.cjs" }, + "typesVersions": {}, "scripts": { "dev": "vite serve dev", - "build": "jiti ../../scripts/build.ts --ssr --dev", + "build": "jiti ../../scripts/build.ts --dev", "test": "vitest -c ../../configs/vitest.config.ts", "test:ssr": "pnpm run test --mode ssr" }, diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index 17b0412ba..cd6a66a30 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,19 +1,34 @@ -import { Accessor, createMemo, JSXElement } from "solid-js"; +import { Accessor, createMemo, JSX, untrack } from "solid-js"; -export function createJSXParser(id: string = "solid-parser") { +function resolveChildren(children: unknown, symbol: symbol): unknown { + if (typeof children === "function" && !children.length) + return symbol in children ? children : resolveChildren(children(), symbol); + if (Array.isArray(children)) { + const results: unknown[] = []; + for (let i = 0; i < children.length; i++) { + const result = resolveChildren(children[i], symbol); + Array.isArray(result) ? results.push.apply(results, result) : results.push(result); + } + return results; + } + return children; +} + +export function createJSXParser(id = "solid-parser") { const $TOKEN = Symbol(id); - function createToken( - tokenCallback: (props: TProps) => TToken, - component?: (props: TProps) => JSXElement - ): (props: TProps) => JSXElement { - return (props: TProps) => { - const token = Object.assign( + function createToken( + tokenCallback: (props: Props) => Token, + component?: (props: Props) => JSX.Element + ): (props: Props) => JSX.Element { + return (props: Props) => + Object.assign( component - ? () => component(props) + ? () => untrack(() => component(props)) : () => { process.env.DEV && - console.info(`tokens can only be rendered inside a Parser with id '${id}'`); + // eslint-disable-next-line no-console + console.warn(`tokens can only be rendered inside a Parser with id '${id}'`); return ""; }, { @@ -21,31 +36,18 @@ export function createJSXParser(id: string = "solid-parser") ...tokenCallback(props) } ); - return token; - }; } - function childrenTokens(fn: Accessor): Accessor { + function childrenTokens(fn: Accessor): Accessor { const children = createMemo(fn); - const resolveChild = (child: any): any => { - while (true) { - if (Array.isArray(child)) return child.map(resolveChild).flat(); - if (typeof child !== "function") return child; - if ($TOKEN in child) return child; - child = child(); - } - }; - return createMemo(() => - ([] as any[]) - .concat(children()) - .map(resolveChild) - .flat() - .filter(child => child && $TOKEN in child) - ); + return createMemo(() => { + const resolvedChildren = resolveChildren(children(), $TOKEN); + return Array.isArray(resolvedChildren) ? resolvedChildren : [resolvedChildren]; + }); } - function isToken(value: any) { - return typeof value === "function" && $TOKEN in value && (value as TTokens); + function isToken(value: unknown | Tokens): value is Tokens { + return typeof value === "function" && !value.length && $TOKEN in value; } return { createToken, childrenTokens, isToken, $TOKEN }; diff --git a/packages/jsx-parser/test/index.test.ts b/packages/jsx-parser/test/index.test.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/jsx-parser/test/server.test.ts b/packages/jsx-parser/test/server.test.ts deleted file mode 100644 index e69de29bb..000000000 From 42a1453894163c126cbb9838bd2a6f5ffe799595 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 26 Jan 2023 19:34:18 +0100 Subject: [PATCH 26/28] Make createJSXParser parameters into options object --- packages/jsx-parser/dev/index.tsx | 2 +- packages/jsx-parser/src/index.ts | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index 67846f38d..10caf7f47 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -10,7 +10,7 @@ type Props = { type CustomToken = TokenValue | TokenAdd | TokenSubtract; -const { createToken, childrenTokens } = createJSXParser("calculator"); +const { createToken, childrenTokens } = createJSXParser({ name: "calculator" }); const Calculator: ParentComponent = props => { const tokens = childrenTokens(() => props.children); diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index cd6a66a30..f1ce89112 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -14,8 +14,9 @@ function resolveChildren(children: unknown, symbol: symbol): unknown { return children; } -export function createJSXParser(id = "solid-parser") { - const $TOKEN = Symbol(id); +export function createJSXParser(options?: { name: string }) { + const name = options?.name || process.env.DEV ? "jsx-parser" : ""; + const id = Symbol(name); function createToken( tokenCallback: (props: Props) => Token, @@ -28,11 +29,11 @@ export function createJSXParser(id = "solid-parser") { : () => { process.env.DEV && // eslint-disable-next-line no-console - console.warn(`tokens can only be rendered inside a Parser with id '${id}'`); + console.warn(`tokens can only be rendered inside a Parser with id '${name}'`); return ""; }, { - [$TOKEN]: true, + [id]: true, ...tokenCallback(props) } ); @@ -41,14 +42,14 @@ export function createJSXParser(id = "solid-parser") { function childrenTokens(fn: Accessor): Accessor { const children = createMemo(fn); return createMemo(() => { - const resolvedChildren = resolveChildren(children(), $TOKEN); + const resolvedChildren = resolveChildren(children(), id); return Array.isArray(resolvedChildren) ? resolvedChildren : [resolvedChildren]; }); } function isToken(value: unknown | Tokens): value is Tokens { - return typeof value === "function" && !value.length && $TOKEN in value; + return typeof value === "function" && !value.length && id in value; } - return { createToken, childrenTokens, isToken, $TOKEN }; + return { createToken, childrenTokens, isToken, id }; } From dd5e8ad6135aa349c615d6921702dd508ba5acda Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 26 Jan 2023 19:55:51 +0100 Subject: [PATCH 27/28] Improve resolving children, add jsdoc --- packages/jsx-parser/README.md | 22 ++++++------- packages/jsx-parser/dev/index.tsx | 3 +- packages/jsx-parser/src/index.ts | 51 ++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/packages/jsx-parser/README.md b/packages/jsx-parser/README.md index 81f8e889a..3095cb93b 100644 --- a/packages/jsx-parser/README.md +++ b/packages/jsx-parser/README.md @@ -1,25 +1,25 @@

- Solid Primitives parser + Solid Primitives JSX Parser

# @solid-primitives/parser A primitive to extend the types of values JSX can return. These JSX-elements are named `tokens`. -- [`createJSXParser`](#createJSXParser) — Provides the tools to create and identify `tokens`: `createToken`, `childrenTokens`, `isToken` and `$TOKEN`. +- [`createJSXParser`](#createJSXParser) — Provides the tools to create and identify `tokens`: `createToken`, `childrenTokens`, `isToken` and `id`. - [`createToken`](#createToken) — Instantiates a `token` associated with the corresponding jsx-parser. - [`childrenTokens`](#childrenTokens) — A function similar to Solid's `children()`, but that will only return valid `tokens` created by the corresponding jsx-parser's `createToken` - [`isToken`](#isToken) — A function to validate if an element is a `token` created by the corresponding jsx-parser's `createToken` -- [`$TOKEN`](#$TOKEN) — The symbol unique to the corresponding jsx-parser +- [`id`](#id) — The symbol unique to the corresponding jsx-parser ## Installation ```bash npm install @solid-primitives/jsx-parser # or -yarn add @solid-primitives/parser +yarn add @solid-primitives/jsx-parser # or -pnpm add @solid-primitives/parser +pnpm add @solid-primitives/jsx-parser ``` ## `createJSXParser` @@ -28,7 +28,7 @@ Provides the tools to create and identify `tokens`. ### How to use it -`createJSXParser` takes an optional id as argument, and returns the following functions `createToken`, `childrenTokens`, `isToken` and the symbol associated with the jsx-parser `$TOKEN`. +`createJSXParser` takes an optional id as argument, and returns the following functions `createToken`, `childrenTokens`, `isToken` and the symbol associated with the jsx-parser `id`. It also takes as a generic the union of accepted token-types. @@ -37,7 +37,7 @@ import { createJSXParser } from "@solid-primitives/jsx-parser"; type UnionOfAcceptedTokens = Token1 | Token2 | ... -const {createToken, childrenTokens, isToken, $TOKEN} = createJSXParser('parser-example'); +const {createToken, childrenTokens, isToken, id} = createJSXParser('parser-example'); ``` ## `createToken` @@ -98,7 +98,7 @@ const tokens = childrenTokens(() => props.children); ## `isToken` -A function to validate if a value is a token created by the corresponding jsx-parser, by checking if the value contains the `$TOKEN`-symbol. +A function to validate if a value is a token created by the corresponding jsx-parser, by checking if the value contains the `id`-symbol. ### How to use it @@ -111,17 +111,17 @@ if (!token) return; token; // token is typed as UnionOfAcceptedTokens ``` -## `$TOKEN` +## `id` The symbol which is attached to all tokens of the corresponding jsx-parser. This is internally used in `childrenTokens` and `isToken` to validate if a value is a token. ### How to use it -`$TOKEN` can be used for validation. +`id` can be used for validation. ```tsx const value = props.children[0]; -if (!($TOKEN in value)) return; +if (!(id in value)) return; const token = value as UnionOfAcceptedTokens; ``` diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index 10caf7f47..77ea73a12 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -33,7 +33,7 @@ const Calculator: ParentComponent = props => { return (
- {props.children} = {calculation()} + {tokens()} = {calculation()}
); }; @@ -88,6 +88,7 @@ const App: Component = () => { }} > +

This is a calculator

diff --git a/packages/jsx-parser/src/index.ts b/packages/jsx-parser/src/index.ts index f1ce89112..2695df677 100644 --- a/packages/jsx-parser/src/index.ts +++ b/packages/jsx-parser/src/index.ts @@ -1,19 +1,39 @@ import { Accessor, createMemo, JSX, untrack } from "solid-js"; -function resolveChildren(children: unknown, symbol: symbol): unknown { - if (typeof children === "function" && !children.length) - return symbol in children ? children : resolveChildren(children(), symbol); - if (Array.isArray(children)) { - const results: unknown[] = []; - for (let i = 0; i < children.length; i++) { - const result = resolveChildren(children[i], symbol); - Array.isArray(result) ? results.push.apply(results, result) : results.push(result); - } - return results; +function resolveChildren(resolved: unknown[], children: unknown, symbol: symbol): void { + if (typeof children === "function" && !children.length) { + if (symbol in children) resolved.push(children); + else resolveChildren(resolved, children(), symbol); } - return children; + if (Array.isArray(children)) + for (let i = 0; i < children.length; i++) resolveChildren(resolved, children[i], symbol); } +export type TokenComponent = (() => JSX.Element) & T; + +/** + * Provides the tools to create tokenized components, parse and identify tokens in JSX Elements. + * + * @param options - Additional options for the parser + * @param options.name - The name of the parser, used for debugging + * @returns functions `createToken`, `childrenTokens`, `isToken` and the symbol associated with the jsx-parser `id`. + * @example + * ```tsx + * const { createToken, childrenTokens } = createJSXParser(); + * + * const MyToken = createToken(props => ({ type: "my-token", props })); + * + * const MyComponent = (props) => { + * const tokens = childrenTokens(() => props.children); + * return
    {tokens().map(token =>
  • token.type
  • )}
; + * } + * + * + * + * + * + * ``` + */ export function createJSXParser(options?: { name: string }) { const name = options?.name || process.env.DEV ? "jsx-parser" : ""; const id = Symbol(name); @@ -39,15 +59,16 @@ export function createJSXParser(options?: { name: string }) { ); } - function childrenTokens(fn: Accessor): Accessor { + function childrenTokens(fn: Accessor) { const children = createMemo(fn); return createMemo(() => { - const resolvedChildren = resolveChildren(children(), id); - return Array.isArray(resolvedChildren) ? resolvedChildren : [resolvedChildren]; + const tokens: TokenComponent[] = []; + resolveChildren(tokens, children(), id); + return tokens; }); } - function isToken(value: unknown | Tokens): value is Tokens { + function isToken(value: unknown | TokenComponent): value is TokenComponent { return typeof value === "function" && !value.length && id in value; } From 508826c65340316b8d4589fab6f5fe9693579c06 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 26 Jan 2023 20:33:51 +0100 Subject: [PATCH 28/28] jsx-parser: Add basic tests --- packages/jsx-parser/dev/index.tsx | 30 +++------- packages/jsx-parser/test/index.test.tsx | 72 ++++++++++++++++++++++++ packages/jsx-parser/test/server.test.tsx | 47 ++++++++++++++++ 3 files changed, 127 insertions(+), 22 deletions(-) create mode 100644 packages/jsx-parser/test/index.test.tsx create mode 100644 packages/jsx-parser/test/server.test.tsx diff --git a/packages/jsx-parser/dev/index.tsx b/packages/jsx-parser/dev/index.tsx index 77ea73a12..a3c49461c 100644 --- a/packages/jsx-parser/dev/index.tsx +++ b/packages/jsx-parser/dev/index.tsx @@ -8,9 +8,10 @@ type Props = { children?: JSX.Element | JSX.Element[]; }; -type CustomToken = TokenValue | TokenAdd | TokenSubtract; - -const { createToken, childrenTokens } = createJSXParser({ name: "calculator" }); +const { createToken, childrenTokens } = createJSXParser<{ + id: "Value" | "Add" | "Subtract"; + props: Props; +}>({ name: "calculator" }); const Calculator: ParentComponent = props => { const tokens = childrenTokens(() => props.children); @@ -38,11 +39,6 @@ const Calculator: ParentComponent = props => { ); }; -type TokenValue = { - id: "Value"; - props: Props; -}; - const Value = createToken( (props: Props) => ({ props, @@ -51,26 +47,16 @@ const Value = createToken( props => <>{props.value} ); -type TokenAdd = { - id: "Add"; - props: Props; -}; - -const Add = createToken( - props => ({ +const Add = createToken( + (props: Props) => ({ props, id: "Add" }), props => <> + {props.value} ); -type TokenSubtract = { - id: "Subtract"; - props: Props; -}; - -const Subtract = createToken( - props => ({ +const Subtract = createToken( + (props: Props) => ({ props, id: "Subtract" }), diff --git a/packages/jsx-parser/test/index.test.tsx b/packages/jsx-parser/test/index.test.tsx new file mode 100644 index 000000000..f8bded0ce --- /dev/null +++ b/packages/jsx-parser/test/index.test.tsx @@ -0,0 +1,72 @@ +import { children, createRoot, createSignal, Show } from "solid-js"; +import { describe, expect, it } from "vitest"; +import { createJSXParser } from "../src"; + +describe("jsx-parser", () => { + const parser1 = createJSXParser<{ + type: "my-token"; + props: { text: string }; + }>(); + + const MyToken1 = parser1.createToken((props: { text: string }) => ({ + type: "my-token", + props + })); + + it("should work", () => { + createRoot(() => { + const tokens = parser1.childrenTokens(() => ( + <> + + + + )); + + expect(tokens()).toHaveLength(2); + tokens().forEach(token => expect(token.type).toBe("my-token")); + expect(tokens()[0].props.text).toBe("foo"); + expect(tokens()[1].props.text).toBe("bar"); + + // shouldn't throw + <>{tokens()}; + }); + }); + + it("handled reactive children", () => { + createRoot(() => { + const [show, setShow] = createSignal(true); + + const tokens = parser1.childrenTokens(() => ( + <> + + + + + + )); + + expect(tokens()).toHaveLength(2); + + setShow(false); + + expect(tokens()).toHaveLength(1); + }); + }); + + it("should render tokens", () => { + createRoot(() => { + const parser2 = createJSXParser(); + + const MyToken2 = parser2.createToken( + () => ({}), + (props: { text: string }) =>
{props.text}
+ ); + + const rendered1 = children(() => ); + const rendered2 = children(() => ); + + expect((rendered1() as HTMLElement).outerHTML).toBe("
foo
"); + expect((rendered2() as HTMLElement).outerHTML).toBe("
bar
"); + }); + }); +}); diff --git a/packages/jsx-parser/test/server.test.tsx b/packages/jsx-parser/test/server.test.tsx new file mode 100644 index 000000000..d5ccb843a --- /dev/null +++ b/packages/jsx-parser/test/server.test.tsx @@ -0,0 +1,47 @@ +import { renderToString } from "solid-js/web"; +import { describe, expect, it } from "vitest"; +import { createJSXParser } from "../src"; + +describe("jsx-parser", () => { + const parser1 = createJSXParser<{ + type: "my-token"; + props: { text: string }; + }>(); + + const MyToken1 = parser1.createToken((props: { text: string }) => ({ + type: "my-token", + props + })); + + it("should work", () => { + const tokens = parser1.childrenTokens(() => ( + <> + + + + )); + + expect(tokens()).toHaveLength(2); + tokens().forEach(token => expect(token.type).toBe("my-token")); + expect(tokens()[0].props.text).toBe("foo"); + expect(tokens()[1].props.text).toBe("bar"); + + // shouldn't throw + <>{tokens()}; + }); + + it("should render tokens", () => { + const parser2 = createJSXParser(); + + const MyToken2 = parser2.createToken( + () => ({}), + (props: { text: string }) =>
{props.text}
+ ); + + const rendered1 = renderToString(() => ); + const rendered2 = renderToString(() => ); + + expect(rendered1).toBe("
foo
"); + expect(rendered2).toBe("
bar
"); + }); +});