From 82d784e3b25b505ca2a65f0651c2746521806a05 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 7 Feb 2024 15:18:46 +1100 Subject: [PATCH] wip: experiment --- .gitignore | 2 +- .vscode/settings.json | 3 +- biome.json | 5 ++- bun.lockb | Bin 175792 -> 184408 bytes example/package.json | 3 +- example/src/index.ts | 9 ---- example/src/index.tsx | 24 +++++++++++ package.json | 1 + scripts/preconstruct.ts | 93 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 - src/index.tsx | 79 ++++++++++++++++++++++++++++++++++ src/package.json | 12 ++++-- tsconfig.build.json | 2 +- 13 files changed, 215 insertions(+), 19 deletions(-) delete mode 100644 example/src/index.ts create mode 100644 example/src/index.tsx create mode 100644 scripts/preconstruct.ts delete mode 100644 src/index.ts create mode 100644 src/index.tsx diff --git a/.gitignore b/.gitignore index fc347eff..6f843372 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.map coverage -lib +_lib node_modules tsconfig.*.tsbuildinfo tsconfig.tsbuildinfo \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 223cd892..0e780996 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,5 @@ }, "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" - }, - "css.validate": false + } } diff --git a/biome.json b/biome.json index 64196dbd..917e99be 100644 --- a/biome.json +++ b/biome.json @@ -20,7 +20,10 @@ "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "complexity": { + "noUselessFragments": "off" + } } } } diff --git a/bun.lockb b/bun.lockb index aeb9bd057c44584ba86a71bd90b0de3710b7f3bb..73e1e616eea905aef1e32a559952303c895a4916 100755 GIT binary patch delta 6903 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 IJcoMvAJKKJdjJ3c delta 1800 zcmd^=+e=hY6vod!S?ZW$YH4cCc$soE^OD+4Gog&2Bq6C@B_WA|tcRwTEC_@yQYdY+ z#G+EO8A}HpGt(pq%e>`f2F=p(B`UFp9xS5z)|~a=|IlK-Ip6xNHS3(UJ)rBZ-_t4G z^~AW+tkLwc;FE?KpOMst+WErW$Gk5NeJ(0oUG;6knKyervQbqidTtyxs%c$K7PH=d zBp^~Rn{q{Vd2*zW-C}4vRi16|vj3=9q4%ZkulN%7xr)%mX?63_+h|*f-*~ki5=AlP zd(+L13l@>4{tw?+T~8b1>gy*gUBYv!nLYC7n8?cc^H&RkH&LRhzt-Z(=~PJuW|iEt z1nd_?aZ6qt7r{HKxU>|g&r?v8+su|Bzr*YfGjHSp%&O%uji}+0FYrmaE#fX1-AxC@ zGP}=xeqiy;9>^#sMm$7`h(DxqsgB$AU`DRj^Oc?;8lIv-7So8w+#U$@+fz^!E3+Wv zbC4HgV-^fQ3en1D zv@#2aFT;E~CkL|#_*AgvxVFgx8u5%vYk|V}EZUhFz`VfdoSri?!qa2ZId#Y;j3{}* zr6`~+@N`_AxYA!Fnmn`;>IS1b#6YzW#Vasc+yqrZwB#Ewe3W%i4h(Jbmf3n{v`L@F zN|ab6(KH$HjvLMJ;b62^KeIUa7KnBjU=|O*6+Q~rL1r7^w=)}JmH?InPch7FBYdS& zqHXz}VIp8XL|cw9OM-vIY?N6t@&;yO%uqGNT9h#B3XUEyu<3ayi;|T(rf97vj { - return c.text("Hello Hono!"); -}); - -export default app; diff --git a/example/src/index.tsx b/example/src/index.tsx new file mode 100644 index 00000000..76b6b096 --- /dev/null +++ b/example/src/index.tsx @@ -0,0 +1,24 @@ +/** @jsx jsx */ +/** @jsxImportSource hono/jsx */ +/** @jsxFrag */ + +import { Button, Framework } from "@wevm/framework"; + +const app = new Framework(); + +app.frame("/", () => { + return { + image:
hello
, + intents: ( + <> + + + + ), + }; +}); + +export default { + port: 3001, + fetch: app.fetch, +}; diff --git a/package.json b/package.json index c331f167..dde3b3ce 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "clean": "rimraf src/index.js src/lib src/tsconfig.build.tsbuildinfo", "format": "biome format . --write", "lint": "biome check . --apply", + "preconstruct": "bun run scripts/preconstruct.ts", "test": "vitest", "typecheck": "tsc --noEmit" }, diff --git a/scripts/preconstruct.ts b/scripts/preconstruct.ts new file mode 100644 index 00000000..2ec38ebc --- /dev/null +++ b/scripts/preconstruct.ts @@ -0,0 +1,93 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { glob } from "glob"; + +// Symlinks package sources to dist for local development + +console.log("Setting up packages for development."); + +// Get all package.json files +const packagePaths = await glob("**/package.json", { + ignore: ["**/dist/**", "**/node_modules/**"], +}); + +let count = 0; +for (const packagePath of packagePaths) { + type Package = { + bin?: Record | undefined; + exports?: + | Record + | undefined; + name?: string | undefined; + private?: boolean | undefined; + }; + const file = Bun.file(packagePath); + const packageJson = (await file.json()) as Package; + + // Skip private packages + if (packageJson.private) continue; + if (!packageJson.exports) continue; + + count += 1; + console.log(`${packageJson.name} — ${path.dirname(packagePath)}`); + + const dir = path.resolve(path.dirname(packagePath)); + + // Empty dist directory + const distDirName = "_lib"; + const dist = path.resolve(dir, distDirName); + let files: string[] = []; + try { + files = await fs.readdir(dist); + } catch { + await fs.mkdir(dist); + } + + const promises: Promise[] = []; + for (const file of files) { + promises.push( + fs.rm(path.join(dist, file), { recursive: true, force: true }), + ); + } + await Promise.all(promises); + + // Link exports to dist locations + for (const [key, exports] of Object.entries(packageJson.exports)) { + // Skip `package.json` exports + if (/package\.json$/.test(key)) continue; + + let entries: string[][]; + if (typeof exports === "string") + entries = [ + ["default", exports], + ["types", exports.replace(".js", ".d.ts")], + ]; + else entries = Object.entries(exports); + + // Link exports to dist locations + for (const [, value] of entries as [ + type: "types" | "default", + value: string, + ][]) { + const srcDir = path.resolve( + dir, + path.dirname(value).replace(distDirName, ""), + ); + let srcFileName: string; + if (key === ".") srcFileName = "index.tsx"; + else srcFileName = path.basename(`${key}.tsx`); + const srcFilePath = path.resolve(srcDir, srcFileName); + + const distDir = path.resolve(dir, path.dirname(value)); + const distFileName = path.basename(value); + const distFilePath = path.resolve(distDir, distFileName); + + await fs.mkdir(distDir, { recursive: true }); + + // Symlink src to dist file + await fs.symlink(srcFilePath, distFilePath, "file"); + } + } +} + +console.log(`Done. Set up ${count} ${count === 1 ? "package" : "packages"}.`); diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 1e29c457..00000000 --- a/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export const hello = "world"; diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 00000000..23fdb62f --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,79 @@ +import { type Context, Hono } from "hono"; +import { ImageResponse } from "hono-og"; +import { type JSXNode } from "hono/jsx"; + +type FrameReturnType = { + image: JSX.Element; + intents: JSX.Element; +}; + +export class Framework extends Hono { + frame( + path: string, + handler: (c: Context) => FrameReturnType | Promise, + ) { + this.get(path, async (c) => { + const { intents } = await handler(c); + return c.render( + + + + + + {parseIntents(intents)} + + , + ); + }); + + // TODO: don't slice + this.get(`${path.slice(1)}/_og`, async (c) => { + const { image } = await handler(c); + return new ImageResponse(image); + }); + } +} + +//////////////////////////////////////////////////////////////////////// +// Components + +export type ButtonProps = { + children: string; +}; + +export function Button({ children }: ButtonProps) { + return ; +} + +//////////////////////////////////////////////////////////////////////// +// Utilities + +type Counter = { button: number }; + +function parseIntents(intents_: JSX.Element) { + const intents = intents_ as unknown as JSXNode; + const counter: Counter = { + button: 0, + }; + + if (typeof intents.children[0] === "object") + return Object.assign(intents, { + children: intents.children.map((e) => parseIntent(e as JSXNode, counter)), + }); + return parseIntent(intents, counter); +} + +function parseIntent(node: JSXNode, counter: Counter) { + const intent = ( + typeof node.tag === "function" ? node.tag({}) : node + ) as JSXNode; + + const props = intent.props || {}; + + if (props.property === "fc:frame:button") { + props.property = `fc:frame:button:${counter.button++}`; + props.content = node.children; + } + + return Object.assign(intent, { props }); +} diff --git a/src/package.json b/src/package.json index 87f254c4..9df81242 100644 --- a/src/package.json +++ b/src/package.json @@ -2,11 +2,17 @@ "name": "@wevm/framework", "version": "0.0.0", "type": "module", - "module": "lib/index.js", - "types": "lib/index.d.ts", - "typings": "lib/index.d.ts", + "module": "_lib/index.js", + "types": "_lib/index.d.ts", + "typings": "_lib/index.d.ts", "sideEffects": false, + "exports": { + ".": "./_lib/index.js" + }, "peerDependencies": { "hono": "^3" + }, + "dependencies": { + "hono-og": "~0.0.2" } } diff --git a/tsconfig.build.json b/tsconfig.build.json index d8c2f06e..2bd43d01 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -12,7 +12,7 @@ "declaration": true, "declarationMap": true, "sourceMap": true, - "outDir": "./src/lib", + "outDir": "./src/_lib", "rootDir": "./src" } } \ No newline at end of file