diff --git a/apps/docs/.eslintrc.js b/apps/docs/.eslintrc.js deleted file mode 100644 index 7d644a4..0000000 --- a/apps/docs/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ["@repo/eslint-config/next.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/apps/docs/README.md b/apps/docs/README.md deleted file mode 100644 index d364535..0000000 --- a/apps/docs/README.md +++ /dev/null @@ -1,28 +0,0 @@ -## Getting Started - -First, run the development server: - -```bash -yarn dev -``` - -Open [http://localhost:3001](http://localhost:3001) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -To create [API routes](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) add an `api/` directory to the `app/` directory with a `route.ts` file. For individual endpoints, create a subfolder in the `api` directory, like `api/hello/route.ts` would map to [http://localhost:3001/api/hello](http://localhost:3001/api/hello). - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/docs/app/favicon.ico b/apps/docs/app/favicon.ico deleted file mode 100644 index 3f804c0..0000000 Binary files a/apps/docs/app/favicon.ico and /dev/null differ diff --git a/apps/docs/app/globals.css b/apps/docs/app/globals.css deleted file mode 100644 index 8eee6cb..0000000 --- a/apps/docs/app/globals.css +++ /dev/null @@ -1,50 +0,0 @@ -:root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", - "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", - "Fira Mono", "Droid Sans Mono", "Courier New", monospace; - - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - - --glow-conic: conic-gradient( - from 180deg at 50% 50%, - #2a8af6 0deg, - #a853ba 180deg, - #e92a67 360deg - ); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -a { - color: inherit; - text-decoration: none; -} diff --git a/apps/docs/app/page.module.css b/apps/docs/app/page.module.css deleted file mode 100644 index 98481c6..0000000 --- a/apps/docs/app/page.module.css +++ /dev/null @@ -1,335 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.vercelLogo { - filter: invert(1); -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.hero { - display: flex; - position: relative; - place-items: center; -} - -.heroContent { - display: flex; - position: relative; - z-index: 0; - padding-bottom: 4rem; - flex-direction: column; - gap: 2rem; - justify-content: space-between; - align-items: center; - width: auto; - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, - "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol", "Noto Color Emoji"; - padding-top: 48px; - - @media (min-width: 768px) { - padding-top: 4rem; - padding-bottom: 6rem; - } - @media (min-width: 1024px) { - padding-top: 5rem; - padding-bottom: 8rem; - } -} - -.logos { - display: flex; - z-index: 50; - justify-content: center; - align-items: center; - width: 100%; -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -.circles { - position: absolute; - min-width: 614px; - min-height: 614px; - pointer-events: none; -} - -.logo { - z-index: 50; - width: 120px; - height: 120px; -} - -.logoGradientContainer { - display: flex; - position: absolute; - z-index: 50; - justify-content: center; - align-items: center; - width: 16rem; - height: 16rem; -} - -.turborepoWordmarkContainer { - display: flex; - z-index: 50; - padding-left: 1.5rem; - padding-right: 1.5rem; - flex-direction: column; - gap: 1.25rem; - justify-content: center; - align-items: center; - text-align: center; - - @media (min-width: 1024px) { - gap: 1.5rem; - } -} - -.turborepoWordmark { - width: 160px; - fill: white; - - @media (min-width: 768px) { - width: 200px; - } -} - -.code { - font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", - monospace; - font-weight: 700; -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -/* Gradients */ -.gradient { - position: absolute; - mix-blend-mode: normal; - will-change: filter; - pointer-events: none; -} - -.gradientSmall { - filter: blur(32px); -} - -.gradientLarge { - filter: blur(75px); -} - -.glowConic { - background-image: var(--glow-conic); -} - -.logoGradient { - opacity: 0.9; - width: 120px; - height: 120px; -} - -.backgroundGradient { - top: -500px; - width: 1000px; - height: 1000px; - opacity: 0.15; -} - -.button { - background-color: #ffffff; - border-radius: 8px; - border-style: none; - box-sizing: border-box; - color: #000000; - cursor: pointer; - display: inline-block; - font-size: 16px; - height: 40px; - line-height: 20px; - list-style: none; - margin: 0; - outline: none; - padding: 10px 16px; - position: relative; - text-align: center; - text-decoration: none; - transition: color 100ms; - vertical-align: baseline; - user-select: none; - -webkit-user-select: none; - touch-action: manipulation; -} - -.button:hover, -.button:focus { - background-color: #e5e4e2; -} diff --git a/apps/docs/app/page.tsx b/apps/docs/app/page.tsx deleted file mode 100644 index 5ca2020..0000000 --- a/apps/docs/app/page.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import Image from "next/image"; -import { Card } from "@repo/ui/card"; -import { Code } from "@repo/ui/code"; -import styles from "./page.module.css"; -import { Button } from "@repo/ui/button"; - -function Gradient({ - conic, - className, - small, -}: { - small?: boolean; - conic?: boolean; - className?: string; -}): JSX.Element { - return ( - - ); -} - -const LINKS = [ - { - title: "Docs", - href: "https://turbo.build/repo/docs", - description: "Find in-depth information about Turborepo features and API.", - }, - { - title: "Learn", - href: "https://turbo.build/repo/docs/handbook", - description: "Learn more about monorepos with our handbook.", - }, - { - title: "Templates", - href: "https://turbo.build/repo/docs/getting-started/from-example", - description: "Choose from over 15 examples and deploy with a single click.", - }, - { - title: "Deploy", - href: "https://vercel.com/new", - description: - "Instantly deploy your Turborepo to a shareable URL with Vercel.", - }, -]; - -export default function Page(): JSX.Element { - return ( -
-
-

- examples/basic  - docs -

-
- - By{" "} - Vercel Logo - -
-
- - - -
-
-
-
- Turborepo -
-
- -
- -
- -
-
- -
- - Turborepo logo - - - - - - - - - - -
-
-
- -
- {LINKS.map(({ title, href, description }) => ( - - {description} - - ))} -
-
- ); -} diff --git a/apps/docs/next-env.d.ts b/apps/docs/next-env.d.ts deleted file mode 100644 index 4f11a03..0000000 --- a/apps/docs/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/docs/next.config.js b/apps/docs/next.config.js deleted file mode 100644 index a5b0aec..0000000 --- a/apps/docs/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -module.exports = { - transpilePackages: ["@repo/ui"], -}; diff --git a/apps/docs/package.json b/apps/docs/package.json deleted file mode 100644 index ad719a3..0000000 --- a/apps/docs/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "next dev --port 3001", - "build": "next build", - "start": "next start", - "lint": "eslint . --max-warnings 0" - }, - "dependencies": { - "@repo/ui": "*", - "next": "^14.0.4", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@next/eslint-plugin-next": "^14.0.4", - "@repo/eslint-config": "*", - "@repo/typescript-config": "*", - "@types/eslint": "^8.56.1", - "@types/node": "^20.10.6", - "@types/react": "^18.2.46", - "@types/react-dom": "^18.2.18", - "eslint": "^8.56.0", - "typescript": "^5.3.3" - } -} diff --git a/apps/docs/public/circles.svg b/apps/docs/public/circles.svg deleted file mode 100644 index 6533be5..0000000 --- a/apps/docs/public/circles.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/docs/public/turborepo.svg b/apps/docs/public/turborepo.svg deleted file mode 100644 index 2f9aa1f..0000000 --- a/apps/docs/public/turborepo.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json deleted file mode 100644 index 24e7548..0000000 --- a/apps/docs/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "@repo/typescript-config/nextjs.json", - "compilerOptions": { - "plugins": [ - { - "name": "next" - } - ] - }, - "include": [ - "next-env.d.ts", - "next.config.js", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] -} diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js deleted file mode 100644 index 7d644a4..0000000 --- a/apps/web/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ["@repo/eslint-config/next.js"], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, -}; diff --git a/apps/web/app/favicon.ico b/apps/web/app/favicon.ico deleted file mode 100644 index 3f804c0..0000000 Binary files a/apps/web/app/favicon.ico and /dev/null differ diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css deleted file mode 100644 index 8eee6cb..0000000 --- a/apps/web/app/globals.css +++ /dev/null @@ -1,50 +0,0 @@ -:root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", - "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", - "Fira Mono", "Droid Sans Mono", "Courier New", monospace; - - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - - --glow-conic: conic-gradient( - from 180deg at 50% 50%, - #2a8af6 0deg, - #a853ba 180deg, - #e92a67 360deg - ); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -a { - color: inherit; - text-decoration: none; -} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx deleted file mode 100644 index 5f90d11..0000000 --- a/apps/web/app/layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import "./globals.css"; -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; - -const inter = Inter({ subsets: ["latin"] }); - -export const metadata: Metadata = { - title: "Create Turborepo", - description: "Generated by create turbo", -}; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}): JSX.Element { - return ( - - {children} - - ); -} diff --git a/apps/web/app/page.module.css b/apps/web/app/page.module.css deleted file mode 100644 index 98481c6..0000000 --- a/apps/web/app/page.module.css +++ /dev/null @@ -1,335 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.vercelLogo { - filter: invert(1); -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.hero { - display: flex; - position: relative; - place-items: center; -} - -.heroContent { - display: flex; - position: relative; - z-index: 0; - padding-bottom: 4rem; - flex-direction: column; - gap: 2rem; - justify-content: space-between; - align-items: center; - width: auto; - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, - "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol", "Noto Color Emoji"; - padding-top: 48px; - - @media (min-width: 768px) { - padding-top: 4rem; - padding-bottom: 6rem; - } - @media (min-width: 1024px) { - padding-top: 5rem; - padding-bottom: 8rem; - } -} - -.logos { - display: flex; - z-index: 50; - justify-content: center; - align-items: center; - width: 100%; -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -.circles { - position: absolute; - min-width: 614px; - min-height: 614px; - pointer-events: none; -} - -.logo { - z-index: 50; - width: 120px; - height: 120px; -} - -.logoGradientContainer { - display: flex; - position: absolute; - z-index: 50; - justify-content: center; - align-items: center; - width: 16rem; - height: 16rem; -} - -.turborepoWordmarkContainer { - display: flex; - z-index: 50; - padding-left: 1.5rem; - padding-right: 1.5rem; - flex-direction: column; - gap: 1.25rem; - justify-content: center; - align-items: center; - text-align: center; - - @media (min-width: 1024px) { - gap: 1.5rem; - } -} - -.turborepoWordmark { - width: 160px; - fill: white; - - @media (min-width: 768px) { - width: 200px; - } -} - -.code { - font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", - monospace; - font-weight: 700; -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -/* Gradients */ -.gradient { - position: absolute; - mix-blend-mode: normal; - will-change: filter; - pointer-events: none; -} - -.gradientSmall { - filter: blur(32px); -} - -.gradientLarge { - filter: blur(75px); -} - -.glowConic { - background-image: var(--glow-conic); -} - -.logoGradient { - opacity: 0.9; - width: 120px; - height: 120px; -} - -.backgroundGradient { - top: -500px; - width: 1000px; - height: 1000px; - opacity: 0.15; -} - -.button { - background-color: #ffffff; - border-radius: 8px; - border-style: none; - box-sizing: border-box; - color: #000000; - cursor: pointer; - display: inline-block; - font-size: 16px; - height: 40px; - line-height: 20px; - list-style: none; - margin: 0; - outline: none; - padding: 10px 16px; - position: relative; - text-align: center; - text-decoration: none; - transition: color 100ms; - vertical-align: baseline; - user-select: none; - -webkit-user-select: none; - touch-action: manipulation; -} - -.button:hover, -.button:focus { - background-color: #e5e4e2; -} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx deleted file mode 100644 index 95186e8..0000000 --- a/apps/web/app/page.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import Image from "next/image"; -import { Card } from "@repo/ui/card"; -import { Code } from "@repo/ui/code"; -import styles from "./page.module.css"; -import { Button } from "@repo/ui/button"; - -function Gradient({ - conic, - className, - small, -}: { - small?: boolean; - conic?: boolean; - className?: string; -}): JSX.Element { - return ( - - ); -} - -const LINKS = [ - { - title: "Docs", - href: "https://turbo.build/repo/docs", - description: "Find in-depth information about Turborepo features and API.", - }, - { - title: "Learn", - href: "https://turbo.build/repo/docs/handbook", - description: "Learn more about monorepos with our handbook.", - }, - { - title: "Templates", - href: "https://turbo.build/repo/docs/getting-started/from-example", - description: "Choose from over 15 examples and deploy with a single click.", - }, - { - title: "Deploy", - href: "https://vercel.com/new", - description: - "Instantly deploy your Turborepo to a shareable URL with Vercel.", - }, -]; - -export default function Page(): JSX.Element { - return ( -
-
-

- examples/basic  - web -

- -
- - - -
-
-
-
- -
-
- -
- -
- Turborepo -
-
- -
- - Turborepo logo - - - - - - - - - - -
-
-
- -
- {LINKS.map(({ title, href, description }) => ( - - {description} - - ))} -
-
- ); -} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts deleted file mode 100644 index 4f11a03..0000000 --- a/apps/web/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/web/next.config.js b/apps/web/next.config.js deleted file mode 100644 index a5b0aec..0000000 --- a/apps/web/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -module.exports = { - transpilePackages: ["@repo/ui"], -}; diff --git a/apps/web/package.json b/apps/web/package.json deleted file mode 100644 index cd92122..0000000 --- a/apps/web/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "web", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint . --max-warnings 0" - }, - "dependencies": { - "@repo/ui": "*", - "next": "^14.0.4", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@next/eslint-plugin-next": "^14.0.4", - "@repo/eslint-config": "*", - "@repo/typescript-config": "*", - "@types/eslint": "^8.56.1", - "@types/node": "^20.10.6", - "@types/react": "^18.2.46", - "@types/react-dom": "^18.2.18", - "eslint": "^8.56.0", - "typescript": "^5.3.3" - } -} diff --git a/apps/web/public/circles.svg b/apps/web/public/circles.svg deleted file mode 100644 index 6533be5..0000000 --- a/apps/web/public/circles.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/web/public/next.svg b/apps/web/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/apps/web/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/public/turborepo.svg b/apps/web/public/turborepo.svg deleted file mode 100644 index 2f9aa1f..0000000 --- a/apps/web/public/turborepo.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/web/public/vercel.svg b/apps/web/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/apps/web/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json deleted file mode 100644 index 24e7548..0000000 --- a/apps/web/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "@repo/typescript-config/nextjs.json", - "compilerOptions": { - "plugins": [ - { - "name": "next" - } - ] - }, - "include": [ - "next-env.d.ts", - "next.config.js", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] -} diff --git a/bun.lockb b/bun.lockb index 8640756..35aa7a1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/examples/with-next-and-wagmi/.gitignore b/examples/with-next-and-wagmi/.gitignore new file mode 100644 index 0000000..8f322f0 --- /dev/null +++ b/examples/with-next-and-wagmi/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/with-next-and-wagmi/README.md b/examples/with-next-and-wagmi/README.md new file mode 100644 index 0000000..bd3aa7b --- /dev/null +++ b/examples/with-next-and-wagmi/README.md @@ -0,0 +1 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-wagmi`](https://github.com/wevm/wagmi/tree/main/packages/create-wagmi). diff --git a/examples/with-next-and-wagmi/next.config.js b/examples/with-next-and-wagmi/next.config.js new file mode 100644 index 0000000..767719f --- /dev/null +++ b/examples/with-next-and-wagmi/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/examples/with-next-and-wagmi/package.json b/examples/with-next-and-wagmi/package.json new file mode 100644 index 0000000..9d4a693 --- /dev/null +++ b/examples/with-next-and-wagmi/package.json @@ -0,0 +1,33 @@ +{ + "name": "with-next-and-wagmi", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@tanstack/react-query": "5.0.5", + "next": "13.5.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "viem": "latest", + "wagmi": "latest" + }, + "devDependencies": { + "@evmos/dappstore-sdk": "workspace:*", + "@types/node": "^20.7.2", + "@types/react": "^18.2.22", + "@types/react-dom": "^18.2.7", + "@wagmi/cli": "latest", + "bufferutil": "^4.0.7", + "encoding": "^0.1.13", + "lokijs": "^1.5.12", + "pino-pretty": "^10.2.0", + "supports-color": "^9.4.0", + "typescript": "^5.2.2", + "utf-8-validate": "^6.0.3" + } +} diff --git a/examples/with-next-and-wagmi/src/app/globals.css b/examples/with-next-and-wagmi/src/app/globals.css new file mode 100644 index 0000000..0733a7e --- /dev/null +++ b/examples/with-next-and-wagmi/src/app/globals.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/examples/with-next-and-wagmi/src/app/layout.tsx b/examples/with-next-and-wagmi/src/app/layout.tsx new file mode 100644 index 0000000..c591052 --- /dev/null +++ b/examples/with-next-and-wagmi/src/app/layout.tsx @@ -0,0 +1,23 @@ +import "./globals.css"; +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import { Suspense, type ReactNode } from "react"; + +import { Providers } from "./providers"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Wagmi", + description: "Generated by create-wagmi", +}; + +export default function RootLayout(props: { children: ReactNode }) { + return ( + + + {props.children} + + + ); +} diff --git a/examples/with-next-and-wagmi/src/app/page.tsx b/examples/with-next-and-wagmi/src/app/page.tsx new file mode 100644 index 0000000..2670194 --- /dev/null +++ b/examples/with-next-and-wagmi/src/app/page.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { client, dAppstoreClient, provider } from "@/wagmi"; +import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; +import { useEffect, useState, useSyncExternalStore } from "react"; +const createAccountStore = () => { + let accounts: string[] = []; + const listeners = new Set<(accounts: string[]) => void>(); + provider.request({ method: "eth_requestAccounts" }).then((_accounts) => { + accounts = _accounts; + console.log("eth_accounts", _accounts); + listeners.forEach((listener) => listener(_accounts)); + }); + + provider.on("accountsChanged", (_accounts) => { + console.log("accountsChanged", _accounts); + accounts = _accounts; + listeners.forEach((listener) => listener(_accounts)); + }); + return { + getAccounts: () => accounts, + subscribe: (callback: (accounts: string[]) => void) => { + listeners.add(callback); + return () => { + listeners.delete(callback); + }; + }, + }; +}; +const accountStore = createAccountStore(); +// const useAccount = () => { +// const [accounts, setAccounts] = useState([]); +// useEffect(() => { +// provider.request({ method: "eth_accounts" }).then((accounts) => { +// setAccounts(accounts); +// }); +// const listener = (accounts: string[]) => { +// setAccounts(accounts); +// }; +// provider.on("accountsChanged", listener); + +// return () => { +// provider.removeListener("accountsChanged", listener); +// }; +// }, []); +// return accounts; +// }; + +const useChainId = () => { + const [chainId, setChainId] = useState(null); + useEffect(() => { + provider.request({ method: "eth_chainId" }).then((chainId) => { + setChainId(chainId); + }); + + const listener = (chainId: string) => { + setChainId(chainId); + }; + provider.on("chainChanged", listener); + + return () => { + provider.removeListener("chainChanged", listener); + }; + }, []); + return chainId; +}; + +function App() { + const accounts = useSyncExternalStore( + accountStore.subscribe, + accountStore.getAccounts, + accountStore.getAccounts + ); + const chainId = useChainId(); + + return ( + <> +
+

Account

+
+ Address:{" "} + {accounts + ?.map((address) => `${address.slice(0, 4)}...${address.slice(-8)}`) + .join(", ")} +
+ Chain ID: {chainId} +
+
+ + ); +} + +export default App; diff --git a/examples/with-next-and-wagmi/src/app/providers.tsx b/examples/with-next-and-wagmi/src/app/providers.tsx new file mode 100644 index 0000000..d4ba95f --- /dev/null +++ b/examples/with-next-and-wagmi/src/app/providers.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState, type ReactNode } from "react"; +import { WagmiProvider } from "wagmi"; + +import {} from "@/wagmi"; + +export function Providers(props: { children: ReactNode }) { + const [queryClient] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + refetchOnMount: true, + }, + }, + }) + ); + + return ( + + {props.children} + + ); +} diff --git a/examples/with-next-and-wagmi/src/wagmi.ts b/examples/with-next-and-wagmi/src/wagmi.ts new file mode 100644 index 0000000..92c4563 --- /dev/null +++ b/examples/with-next-and-wagmi/src/wagmi.ts @@ -0,0 +1,7 @@ +import { createDAppstoreClient } from "@evmos/dappstore-sdk"; +import { createWalletClient, custom } from "viem"; +export const dAppstoreClient = createDAppstoreClient(); +export const provider = dAppstoreClient.provider; +export const client = createWalletClient({ + transport: custom(provider), +}); diff --git a/examples/with-next-and-wagmi/tsconfig.json b/examples/with-next-and-wagmi/tsconfig.json new file mode 100644 index 0000000..e59724b --- /dev/null +++ b/examples/with-next-and-wagmi/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/with-next/.gitignore b/examples/with-next/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/examples/with-next/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/web/README.md b/examples/with-next/README.md similarity index 55% rename from apps/web/README.md rename to examples/with-next/README.md index 3d7b63a..c403366 100644 --- a/apps/web/README.md +++ b/examples/with-next/README.md @@ -1,28 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + ## Getting Started First, run the development server: ```bash +npm run dev +# or yarn dev +# or +pnpm dev +# or +bun dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. -To create [API routes](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) add an `api/` directory to the `app/` directory with a `route.ts` file. For individual endpoints, create a subfolder in the `api` directory, like `api/hello/route.ts` would map to [http://localhost:3000/api/hello](http://localhost:3000/api/hello). +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. ## Learn More To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! ## Deploy on Vercel -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/with-next/next.config.mjs b/examples/with-next/next.config.mjs new file mode 100644 index 0000000..4678774 --- /dev/null +++ b/examples/with-next/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/examples/with-next/package.json b/examples/with-next/package.json new file mode 100644 index 0000000..7a2b06d --- /dev/null +++ b/examples/with-next/package.json @@ -0,0 +1,26 @@ +{ + "name": "with-next", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "react": "^18", + "react-dom": "^18", + "next": "14.1.0" + }, + "devDependencies": { + "@evmos/dappstore-sdk": "workspace:*", + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.0.1", + "postcss": "^8", + "tailwindcss": "^3.3.0" + } +} diff --git a/examples/with-next/postcss.config.js b/examples/with-next/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/examples/with-next/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/docs/public/next.svg b/examples/with-next/public/next.svg similarity index 100% rename from apps/docs/public/next.svg rename to examples/with-next/public/next.svg diff --git a/apps/docs/public/vercel.svg b/examples/with-next/public/vercel.svg similarity index 100% rename from apps/docs/public/vercel.svg rename to examples/with-next/public/vercel.svg diff --git a/examples/with-next/src/app/favicon.ico b/examples/with-next/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/examples/with-next/src/app/favicon.ico differ diff --git a/examples/with-next/src/app/globals.css b/examples/with-next/src/app/globals.css new file mode 100644 index 0000000..875c01e --- /dev/null +++ b/examples/with-next/src/app/globals.css @@ -0,0 +1,33 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/apps/docs/app/layout.tsx b/examples/with-next/src/app/layout.tsx similarity index 79% rename from apps/docs/app/layout.tsx rename to examples/with-next/src/app/layout.tsx index 5f90d11..3314e47 100644 --- a/apps/docs/app/layout.tsx +++ b/examples/with-next/src/app/layout.tsx @@ -1,19 +1,19 @@ -import "./globals.css"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; +import "./globals.css"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: "Create Turborepo", - description: "Generated by create turbo", + title: "Create Next App", + description: "Generated by create next app", }; export default function RootLayout({ children, -}: { +}: Readonly<{ children: React.ReactNode; -}): JSX.Element { +}>) { return ( {children} diff --git a/examples/with-next/src/app/page.tsx b/examples/with-next/src/app/page.tsx new file mode 100644 index 0000000..09d4d1a --- /dev/null +++ b/examples/with-next/src/app/page.tsx @@ -0,0 +1,40 @@ +"use client"; +import Image from "next/image"; +import { createDAppstoreClient } from "@evmos/dappstore-sdk"; + +const client = createDAppstoreClient(); +function App() { + // const [host, setState] = useState(0); + + return ( +
+ +
+ ); +} + +export default function Home() { + return ( +
+
+ +
+
+ ); +} diff --git a/examples/with-next/tailwind.config.ts b/examples/with-next/tailwind.config.ts new file mode 100644 index 0000000..e9a0944 --- /dev/null +++ b/examples/with-next/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/examples/with-next/tsconfig.json b/examples/with-next/tsconfig.json new file mode 100644 index 0000000..7b28589 --- /dev/null +++ b/examples/with-next/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/with-tailwind/.gitignore b/examples/with-tailwind/.gitignore new file mode 100644 index 0000000..468f82a --- /dev/null +++ b/examples/with-tailwind/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/examples/with-tailwind/README.md b/examples/with-tailwind/README.md new file mode 100644 index 0000000..b5a0e62 --- /dev/null +++ b/examples/with-tailwind/README.md @@ -0,0 +1,15 @@ +# @evmos/demo-with-tailwind + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.0.25. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/examples/with-tailwind/package.json b/examples/with-tailwind/package.json new file mode 100644 index 0000000..d42b168 --- /dev/null +++ b/examples/with-tailwind/package.json @@ -0,0 +1,13 @@ +{ + "name": "@evmos/demo-with-tailwind", + "type": "module", + "devDependencies": {}, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "autoprefixer": "^10.4.17", + "postcss": "^8.4.34", + "tailwindcss": "^3.4.1" + } +} diff --git a/examples/with-tailwind/postcss.config.js b/examples/with-tailwind/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/examples/with-tailwind/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/examples/with-tailwind/src/index.css b/examples/with-tailwind/src/index.css new file mode 100644 index 0000000..65dd5f6 --- /dev/null +++ b/examples/with-tailwind/src/index.css @@ -0,0 +1 @@ +@tailwind utilities; diff --git a/examples/with-tailwind/src/index.tsx b/examples/with-tailwind/src/index.tsx new file mode 100644 index 0000000..1d8a0f9 --- /dev/null +++ b/examples/with-tailwind/src/index.tsx @@ -0,0 +1,54 @@ +import "./index.css"; +import { useEffect } from "react"; +import { useAccount, useReadContracts } from "wagmi"; +import { erc20Abi, formatUnits } from "viem"; +// import { createClient } from "@evmos/dappstore-sdk"; +const EVMOS_ERC20_ADDRESS = "0xD4949664cD82660AaE99bEdc034a0deA8A0bd517"; +// const client = createClient(); +export default function Widget() { + const { address, isConnected } = useAccount(); + + const { + data: ercTokenBalance, + error, + isFetching, + } = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: EVMOS_ERC20_ADDRESS, + abi: erc20Abi, + functionName: "balanceOf", + args: address ? [address] : undefined, + }, + { + address: EVMOS_ERC20_ADDRESS, + abi: erc20Abi, + functionName: "decimals", + }, + ], + }); + + // useEffect(() => { + // // console.log(client); + // client.request("ping"); + // return client.subscribe(); + // }, []); + const shortenedAddress = address + ? `${address.slice(0, 4)}...${address.slice(-4)}` + : null; + + return ( +
+

Hello, {isConnected ? shortenedAddress : "World!"}

+ {isFetching &&

Loading balance

} + {ercTokenBalance && ( +

+ Your Evmos Balance is:{" "} + {formatUnits(ercTokenBalance[0], ercTokenBalance[1])} +

+ )} + {error &&

Error: {error.name}

} +
+ ); +} diff --git a/examples/with-tailwind/tailwind.config.js b/examples/with-tailwind/tailwind.config.js new file mode 100644 index 0000000..cb70e6e --- /dev/null +++ b/examples/with-tailwind/tailwind.config.js @@ -0,0 +1,14 @@ +/** @type {import('tailwindcss').Config} */ +export default { + prefix: "wg-", + content: ["./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + colors: { + testcolor: "purple", + }, + }, + }, + + plugins: [], +}; diff --git a/examples/with-tailwind/tsconfig.json b/examples/with-tailwind/tsconfig.json new file mode 100644 index 0000000..0ac23d5 --- /dev/null +++ b/examples/with-tailwind/tsconfig.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + + "compilerOptions": { + "jsx": "react-jsx", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "declaration": true, + "declarationMap": true, + + "isolatedModules": true, + "outDir": "dist", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + }, + "include": ["src"], + "exclude": ["node_modules", "dist"], +} diff --git a/examples/with-vite/.eslintrc.cjs b/examples/with-vite/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/examples/with-vite/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/examples/with-vite/.gitignore b/examples/with-vite/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/examples/with-vite/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/with-vite/README.md b/examples/with-vite/README.md new file mode 100644 index 0000000..0d6babe --- /dev/null +++ b/examples/with-vite/README.md @@ -0,0 +1,30 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/examples/with-vite/index.html b/examples/with-vite/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/examples/with-vite/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/examples/with-vite/package.json b/examples/with-vite/package.json new file mode 100644 index 0000000..1f20c60 --- /dev/null +++ b/examples/with-vite/package.json @@ -0,0 +1,29 @@ +{ + "name": "with-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@evmos/dappstore-sdk": "workspace:*", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.56.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "typescript": "^5.2.2", + "vite": "^5.1.0" + } +} diff --git a/examples/with-vite/public/vite.svg b/examples/with-vite/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/examples/with-vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/with-vite/src/App.css b/examples/with-vite/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/examples/with-vite/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/examples/with-vite/src/App.tsx b/examples/with-vite/src/App.tsx new file mode 100644 index 0000000..17e6cc6 --- /dev/null +++ b/examples/with-vite/src/App.tsx @@ -0,0 +1,25 @@ +import { useState } from "react"; +import reactLogo from "./assets/react.svg"; +import viteLogo from "/vite.svg"; +import "./App.css"; +import { trpcClient } from "@evmos/dappstore-sdk-router/client"; +function App() { + const [count, setCount] = useState(0); + + return ( +
+ +
+ ); +} + +export default App; diff --git a/examples/with-vite/src/assets/react.svg b/examples/with-vite/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/examples/with-vite/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/with-vite/src/index.css b/examples/with-vite/src/index.css new file mode 100644 index 0000000..6119ad9 --- /dev/null +++ b/examples/with-vite/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/with-vite/src/main.tsx b/examples/with-vite/src/main.tsx new file mode 100644 index 0000000..3d7150d --- /dev/null +++ b/examples/with-vite/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/examples/with-vite/src/vite-env.d.ts b/examples/with-vite/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/examples/with-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/with-vite/tsconfig.json b/examples/with-vite/tsconfig.json new file mode 100644 index 0000000..a7fc6fb --- /dev/null +++ b/examples/with-vite/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/with-vite/tsconfig.node.json b/examples/with-vite/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/examples/with-vite/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/with-vite/vite.config.ts b/examples/with-vite/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/examples/with-vite/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/package.json b/package.json index db783f9..50c4cd4 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,34 @@ { - "name": "dappstore", + "name": "dappstore-sdk", "private": true, "scripts": { - "build": "turbo build", + "build": "turbo build --filter=./packages/*", "dev": "turbo dev", "lint": "turbo lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"" + "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "dev:setup-cli": "npm link" }, "devDependencies": { - "@repo/eslint-config": "*", - "@repo/typescript-config": "*", + "@evmos/dappstore-cli": "workspace:*", + "@evmos/config": "workspace:*", + "@types/bun": "^1.0.4", + "autoprefixer": "^10.4.17", + "postcss": "^8.4.35", "prettier": "^3.1.1", + "tailwindcss": "^3.4.1", "turbo": "latest" }, "engines": { "node": ">=18" }, - "packageManager": "bun@1.0.25", + "bin": { + "dappstore": "./" + }, "workspaces": [ - "apps/*", - "packages/*" - ] + "packages/*", + "examples/*" + ], + "dependencies": { + "next": "^14.1.0" + } } diff --git a/packages/dappstore-cli/build.ts b/packages/dappstore-cli/build.ts new file mode 100644 index 0000000..ec3dcea --- /dev/null +++ b/packages/dappstore-cli/build.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +import Bun from "bun"; +import { dependencies, peerDependencies } from "./package.json"; +import { version } from "./package.json"; +import { watch } from "node:fs"; +export const build = async () => + await Bun.build({ + entrypoints: ["./src/index.ts"], + target: "node", + format: "esm", + external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)], + outdir: "./dist", + }); +const test = await build(); + +if (process.argv.includes("--watch")) { + const srcWatcher = watch( + `${import.meta.dir}/src`, + { recursive: true }, + async (event, filename) => { + await build(); + + console.log(`Detected ${event} in ${filename} (src)`); + } + ); + process.on("SIGINT", () => { + srcWatcher.close(); + process.exit(0); + }); +} diff --git a/packages/dappstore-cli/package.json b/packages/dappstore-cli/package.json new file mode 100644 index 0000000..d46b81d --- /dev/null +++ b/packages/dappstore-cli/package.json @@ -0,0 +1,42 @@ +{ + "name": "@evmos/dappstore-cli", + "version": "0.0.0", + "private": false, + "type": "module", + "main": "dist/index.js", + "scripts": { + "build": "bun run ./build.ts" + }, + "bin": { + "dappstore": "./dist/index.js" + }, + "peerDependencies": {}, + "devDependencies": { + "@evmos/dappstore-sdk": "workspace:*", + "@evmos/config": "workspace:*", + "@types/eslint": "^8.56.1", + "@types/node": "^20.10.6", + "@types/react": "^18.2.46", + "@types/react-dom": "^18.2.18", + "eslint": "^8.56.0", + "react": "^18.2.0", + "typescript": "^5.3.3", + "@commander-js/extra-typings": "^11.1.0" + }, + "dependencies": { + "@evmos/dev-wrapper": "workspace:*", + "@types/express": "^4.17.21", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.17", + "chalk": "^5.3.0", + "clsx": "^2.1.0", + "express": "^4.18.2", + "glob": "^10.3.10", + "postcss": "^8.4.33", + "postcss-scopify": "^0.1.10", + "react-dom": "^18.2.0", + "tailwindcss": "^3.4.1", + "ts-dedent": "^2.2.0", + "vite": "^5.0.12" + } +} diff --git a/packages/dappstore-cli/postcss.config.js b/packages/dappstore-cli/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/packages/dappstore-cli/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/dappstore-cli/src/dev.ts b/packages/dappstore-cli/src/dev.ts new file mode 100644 index 0000000..70afdc7 --- /dev/null +++ b/packages/dappstore-cli/src/dev.ts @@ -0,0 +1,20 @@ +import { program } from "@commander-js/extra-typings"; +import { serve } from "@evmos/dev-wrapper/serve/serve.js"; + +program + .command("dev") + .option("-p, --port ", "Development server port", "1337") + .option( + "-t, --target ", + "The port or url of your widget server", + "3000" + ) + + .action(async ({ port, target }) => { + const app = serve({ + target, + }); + app.listen(port, () => { + console.log(`Example app listening on port http://localhost:${port}`); + }); + }); diff --git a/packages/dappstore-cli/src/index.d.ts b/packages/dappstore-cli/src/index.d.ts new file mode 100644 index 0000000..636e1e3 --- /dev/null +++ b/packages/dappstore-cli/src/index.d.ts @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import "./dev.js"; +import "./build.js"; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/dappstore-cli/src/index.ts b/packages/dappstore-cli/src/index.ts new file mode 100644 index 0000000..a1ca3c7 --- /dev/null +++ b/packages/dappstore-cli/src/index.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env node +import "./dev"; + +import { program } from "@commander-js/extra-typings"; +program.parseAsync(process.argv); diff --git a/packages/dappstore-cli/tailwind.config.js b/packages/dappstore-cli/tailwind.config.js new file mode 100644 index 0000000..93d0b65 --- /dev/null +++ b/packages/dappstore-cli/tailwind.config.js @@ -0,0 +1,33 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./src/**/*.{js,jsx,ts,tsx}"], + theme: { + extend: { + colors: { + background: { + // background: rgba(10, 10, 10, 1); + DEFAULT: "rgba(10 10 10 / )", + }, + foreground: { + // background: rgba(237, 224, 220, 1); + DEFAULT: "rgba(237 224 220 / )", + }, + surface: { + // rgba(20, 17, 15, 1) + DEFAULT: "rgba(20 17 15 / )", + }, + primary: { + DEFAULT: "rgba(208 75 0 / )", + container: "rgba(250 92 0 / )", + }, + body: { + DEFAULT: "rgba(180 169 165 / )", + }, + heading: { + DEFAULT: "rgba(250 239 235 / )", + }, + }, + }, + }, + plugins: [], +}; diff --git a/packages/dappstore-cli/tsconfig.json b/packages/dappstore-cli/tsconfig.json new file mode 100644 index 0000000..0ca48f0 --- /dev/null +++ b/packages/dappstore-cli/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@evmos/config/base.json", + "compilerOptions": { + "outDir": "./dist", + "emitDeclarationOnly": true, + + "target": "ESNext", + "moduleResolution": "NodeNext", + "module": "NodeNext" + }, + "include": ["./src/**/*", "./src/**/*.d.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/dappstore-sdk/build.ts b/packages/dappstore-sdk/build.ts new file mode 100644 index 0000000..e1a9aac --- /dev/null +++ b/packages/dappstore-sdk/build.ts @@ -0,0 +1,35 @@ +// @ts-nocheck +import Bun from "bun"; +import { dependencies, peerDependencies } from "./package.json"; +import { version } from "./package.json"; +import { watch } from "node:fs"; +export const build = async () => + await Bun.build({ + entrypoints: ["./src/index.ts", "./src/host.ts"], + target: "browser", + format: "esm", + external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)], + define: { + ["process.env.npm_package_version"]: JSON.stringify(version), + }, + outdir: "./dist", + }); + +await build(); +if (process.argv.includes("--watch")) { + const srcWatcher = watch( + `${import.meta.dir}/src`, + { recursive: true }, + async (event, filename) => { + await build(); + + console.log(`Detected ${event} in ${filename} (src)`); + } + ); + process.on("SIGINT", () => { + srcWatcher.close(); + process.exit(0); + }); +} else { + await build(); +} diff --git a/packages/dappstore-sdk/package.json b/packages/dappstore-sdk/package.json new file mode 100644 index 0000000..78de921 --- /dev/null +++ b/packages/dappstore-sdk/package.json @@ -0,0 +1,45 @@ +{ + "name": "@evmos/dappstore-sdk", + "version": "0.0.0", + "type": "module", + "sideEffects": false, + "private": false, + "scripts": { + "build:types": "tsc -p ./tsconfig.json", + "dev:types": "tsc -p ./tsconfig.json --watch", + "dev": "bun run --watch ./build.ts --watch & bun run dev:types", + "build": "bun run ./build.ts & tsc" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./internal/host": { + "types": "./dist/host.d.ts", + "import": "./dist/host.js" + } + }, + "devDependencies": { + "@evmos/config": "workspace:*", + "@types/node": "^20.10.6", + "@types/react": "^18.2.46", + "@types/react-dom": "^18.2.18", + "typescript": "^5.3.3", + "@trpc/client": "next", + "@trpc/react-query": "next", + "@trpc/server": "next", + "@types/lodash-es": "^4.17.12", + "@types/uuid": "^9.0.8", + "lodash-es": "^4.17.21", + "uuid": "^9.0.1" + }, + "peerDependencies": { + "viem": ">=2.5", + "wagmi": ">=2.5", + "zod": ">=3" + }, + "dependencies": {} +} diff --git a/packages/dappstore-sdk/src/client.ts b/packages/dappstore-sdk/src/client.ts new file mode 100644 index 0000000..8b68045 --- /dev/null +++ b/packages/dappstore-sdk/src/client.ts @@ -0,0 +1,168 @@ +import { createHost } from "./host"; +import { trpcClient } from "./trpc/client"; + +import { + EIP1193Provider, + ProviderConnectInfo, + ProviderRpcError, + ProviderMessage, +} from "./types/EIP1193Provider"; +import { Hex, EIP1193Provider as StronglyTypedEIP1193Provider } from "viem"; +class SDKProvider implements StronglyTypedEIP1193Provider { + unsubscribers: Record> = {}; + on( + event: "accountsChanged", + listener: (accounts: string[]) => void + ): SDKProvider; + on(event: "chainChanged", listener: (chainId: string) => void): SDKProvider; + on( + event: "connect", + listener: (connectInfo: ProviderConnectInfo) => void + ): SDKProvider; + on( + event: "disconnect", + listener: (error: ProviderRpcError) => void + ): SDKProvider; + on( + event: "message", + listener: (message: ProviderMessage) => void + ): SDKProvider; + on(event: string, listener: (...args: any[]) => void): SDKProvider { + if ( + event === "accountsChanged" || + event === "chainChanged" || + event === "connect" + // event === "disconnect" || + // event === "message" + ) { + const { unsubscribe } = trpcClient.provider.on[event].subscribe( + undefined, + { + onData(data) { + listener(data); + }, + } + ); + const unsubMap = this.unsubscribers[event] || new Map(); + unsubMap.set(listener, unsubscribe); + this.unsubscribers[event] = unsubMap; + } + + return this; + } + + removeListener( + event: string, + callback: (...args: any[]) => void + ): EIP1193Provider { + this.unsubscribers[event]?.get(callback)?.(); + this.unsubscribers[event]?.delete(callback); + return this; + } + request: StronglyTypedEIP1193Provider["request"] = (args) => { + if (typeof window === "undefined") { + throw new Error("Cannot call request outside of browser"); + } + return trpcClient.provider.request.mutate(args as never) as never; + }; +} + +class Client { + private _host: null | ReturnType = null; + private _listeners: Record> = {}; + private _provider = new SDKProvider(); + private _ready = false; + private _chainId: Hex | null = null; + private _accounts: Hex[] = []; + private _state: "uninitialized" | "ready" | "error" = "uninitialized"; + get ready() { + return this._ready; + } + get accounts() { + return this._accounts; + } + get chainId() { + return this._chainId; + } + + get provider() { + return this._provider; + } + private get isInsideIframe() { + try { + return window.self !== window.top; + } catch (e) { + return true; + } + } + init() { + if (this.isInsideIframe === false) { + throw new Error("Cannot use DAppstore SDK outside of an iframe"); + } + this.ack(); + const unsubAccounts = trpcClient.provider.on.accountsChanged.subscribe( + undefined, + { + onData: (data) => { + this._accounts = data; + if (this._listeners.accountsChanged) { + this._listeners.accountsChanged.forEach((listener) => { + listener(data); + }); + } + }, + } + ); + + const unsubChain = trpcClient.provider.on.chainChanged.subscribe( + undefined, + { + onData: (data) => { + this._chainId = data; + if (this._listeners.chainChanged) { + this._listeners.chainChanged.forEach((listener) => { + listener(data); + }); + } + }, + } + ); + return () => { + unsubAccounts.unsubscribe(); + unsubChain.unsubscribe(); + }; + } + onAccountsChange(cb: (accounts: string[]) => void) { + if (!this._listeners.accountsChanged) { + this._listeners.accountsChanged = new Set(); + } + this._listeners.accountsChanged.add(cb); + return () => { + this._listeners.accountsChanged?.delete(cb); + }; + } + + onChainChange(cb: (chainId: string) => void) { + if (!this._listeners.chainChanged) { + this._listeners.chainChanged = new Set(); + } + this._listeners.chainChanged.add(cb); + return () => { + this._listeners.chainChanged?.delete(cb); + }; + } + async ack() { + const { state } = await trpcClient.ack.query({ + version: process.env.npm_package_version as string, + }); + this._state = "ready"; + this._accounts = state.accounts; + this._chainId = state.chainId; + } +} + +export const createDAppstoreClient = ({ autoInit = true } = {}) => { + const client = new Client(); + if (autoInit) client.init(); + return client; +}; diff --git a/packages/dappstore-sdk/src/host.ts b/packages/dappstore-sdk/src/host.ts new file mode 100644 index 0000000..fba5e8f --- /dev/null +++ b/packages/dappstore-sdk/src/host.ts @@ -0,0 +1,13 @@ +import { createPostMessageHost } from "./post-message-integration/create-post-message-host"; +import { createHostRouter } from "./trpc/host"; + +type Config = { + target: Window; + provider: unknown; +}; +export const createHost = ({ provider, target }: Config) => { + return createPostMessageHost({ + target, + router: createHostRouter(provider), + }); +}; diff --git a/packages/dappstore-sdk/src/index.ts b/packages/dappstore-sdk/src/index.ts new file mode 100644 index 0000000..52ed12b --- /dev/null +++ b/packages/dappstore-sdk/src/index.ts @@ -0,0 +1,2 @@ +export { createDAppstoreClient } from "./client"; +export * from "./types/EIP1193Provider"; diff --git a/packages/dappstore-sdk/src/post-message-integration/create-post-message-host.ts b/packages/dappstore-sdk/src/post-message-integration/create-post-message-host.ts new file mode 100644 index 0000000..34bb6bb --- /dev/null +++ b/packages/dappstore-sdk/src/post-message-integration/create-post-message-host.ts @@ -0,0 +1,261 @@ +import { + AnyRouter, + TRPCError, + callProcedure, + getErrorShape, + getTRPCErrorFromUnknown, + transformTRPCResponse, +} from "@trpc/server"; +import { + TRPCClientOutgoingMessage, + TRPCRequestMessage, + TRPCResponseMessage, + parseTRPCMessage, +} from "@trpc/server/rpc"; +import { isString } from "lodash-es"; +import { z } from "zod"; +import { isObservable } from "@trpc/server/observable"; + +export const createPostMessageHost = ({ + router, + target, +}: { + router: TRouter; + target: Window; +}) => { + const { transformer } = router._def._config; + + function respond(targetId: string, untransformedJSON: TRPCResponseMessage) { + target.postMessage( + { + source: "dappstore-sdk-router", + target: targetId, + messages: [ + transformTRPCResponse(router._def._config, untransformedJSON), + ], + }, + "*" + ); + } + async function handleSubsription( + targetId: string, + msg: TRPCClientOutgoingMessage + ) { + const { id, jsonrpc } = msg; + if (msg.method === "subscription.stop") { + // TODO: "handle stop"; + + respond(targetId, { + id, + jsonrpc, + result: { + type: "stopped", + }, + }); + return; + } + + const { path, input } = msg.params; + const type = msg.method; + + try { + const result = await callProcedure({ + procedures: router._def.procedures, + path, + getRawInput: async () => input, + ctx: {}, + type, + }); + if (!isObservable(result)) { + throw new TRPCError({ + message: `Subscription ${path} did not return an observable`, + code: "INTERNAL_SERVER_ERROR", + }); + } + + const observable = result; + // TODO: Do we ever need to manually unsubscribe here? + const subscription = observable.subscribe({ + next(data) { + respond(targetId, { + id, + jsonrpc, + result: { + type: "data", + data, + }, + }); + }, + error(err) { + const error = getTRPCErrorFromUnknown(err); + + respond(targetId, { + id, + jsonrpc, + error: getErrorShape({ + config: router._def._config, + error, + type, + path, + input, + ctx: {}, + }), + }); + }, + complete() { + respond(targetId, { + id, + jsonrpc, + result: { + type: "stopped", + }, + }); + }, + }); + + respond(targetId, { + id, + jsonrpc, + result: { + type: "started", + }, + }); + } catch (cause) { + const error = getTRPCErrorFromUnknown(cause); + + respond(targetId, { + id, + jsonrpc, + error: getErrorShape({ + config: router._def._config, + error, + type, + path, + input, + ctx: {}, + }), + }); + } + } + async function handleMessage( + targetId: string, + msg: TRPCClientOutgoingMessage + ) { + // const { id, jsonrpc } = msg; + if (msg.id === null) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "`id` is required", + }); + } + + if (msg.method === "subscription" || msg.method === "subscription.stop") { + return handleSubsription(targetId, msg); + } + handleRequest(targetId, msg); + } + async function handleRequest(targetId: string, msg: TRPCRequestMessage) { + const { id, jsonrpc } = msg; + + const { path, input } = msg.params; + const type = msg.method; + + try { + // await ctxPromise; + const result = await callProcedure({ + procedures: router._def.procedures, + path, + getRawInput: async () => input, + ctx: {}, + type, + }); + + respond(targetId, { + id, + jsonrpc, + result: { + type: "data", + data: result, + }, + }); + return; + } catch (cause) { + const error = getTRPCErrorFromUnknown(cause); + // opts.onError?.({ error, path, type, ctx, req, input }); + respond(targetId, { + id, + jsonrpc, + error: getErrorShape({ + config: router._def._config, + error, + type, + path, + input, + ctx: {}, + }), + }); + } + } + + const listener = async (event: MessageEvent) => { + if ( + event.data?.source === "react-devtools-content-script" || + event.data?.target === "metamask-inpage" + ) + return; + if (event.source !== target) { + return; + } + const targetId = event.data?.clientId; + + if (!isString(targetId)) { + return; + } + + try { + const parsed = z + .object({ + target: z.literal("dappstore-sdk-router"), + clientId: z.string(), + messages: z.array(z.unknown()), + }) + .safeParse(event.data); + + if (!parsed.success) return; + + const message = parsed.data.messages; + + const promises = message + .map((raw) => parseTRPCMessage(raw, transformer)) + .map((message) => handleMessage(targetId, message)); + await Promise.all(promises); + } catch (cause) { + const error = new TRPCError({ + code: "PARSE_ERROR", + cause, + }); + + respond(targetId, { + id: null, + error: getErrorShape({ + config: router._def._config, + error, + type: "unknown", + path: undefined, + input: undefined, + ctx: undefined, + }), + }); + } + }; + + if (typeof window === "undefined") { + return () => {}; + } + + window.addEventListener("message", listener, false); + + return () => { + window.removeEventListener("message", listener); + }; +}; diff --git a/packages/dappstore-sdk/src/post-message-integration/post-message-link.ts b/packages/dappstore-sdk/src/post-message-integration/post-message-link.ts new file mode 100644 index 0000000..463b60e --- /dev/null +++ b/packages/dappstore-sdk/src/post-message-integration/post-message-link.ts @@ -0,0 +1,178 @@ +import { Operation, TRPCClientError, TRPCLink } from "@trpc/client"; +import { observable } from "@trpc/server/observable"; +import type { CombinedDataTransformer } from "@trpc/server"; +/** + * TRPC doesn't recommend importing unstable-core-do-not-import, + * but unfortunately, they also don't offer an official API for creating custom links. + * we need to be mindful on updates, but I imagine typescript checks would report any major change. + */ +import { + type TRPCClientIncomingRequest, + type TRPCResponseMessage, + type AnyRouter, + type DataTransformerOptions, + type TRPCClientOutgoingMessage, + type TRPCRequestMessage, + transformResult, +} from "@trpc/server/unstable-core-do-not-import"; +import { getTransformer } from "@trpc/client/unstable-internals"; + +import { v4 } from "uuid"; +import { debounce } from "lodash-es"; +import { z } from "zod"; + +type PostMessageOptions = { + transformer?: DataTransformerOptions; +}; +class PostMessageClient { + id = v4(); + + outgoing: TRPCClientOutgoingMessage[] = []; + pendingRequests: Map< + TRPCRequestMessage["id"], + (data: TRPCResponseMessage) => void + > = new Map(); + + constructor() {} + request = ( + op: Operation, + resolve: (data: TRPCResponseMessage) => void, + observer: { + complete: () => void; + } + ) => { + const { type, input, path, id } = op; + + const envelope: TRPCRequestMessage = { + id: v4(), + method: type, + params: { + input, + path, + }, + }; + this.pendingRequests.set(envelope.id, resolve); + this.outgoing.push(envelope); + + this.dispatch(); + + return () => { + this.outgoing = this.outgoing.filter((msg) => msg.id !== envelope.id); + observer.complete(); + if (op.type === "subscription") { + this.outgoing.push({ + id, + method: "subscription.stop", + }); + this.dispatch(); + } + }; + }; + + dispatch = debounce( + () => { + if (typeof window === "undefined") return; + window.parent.postMessage( + { + target: "dappstore-sdk-router", + clientId: this.id, + messages: this.outgoing, + }, + "*" + ); + console.log("dispatched", this.outgoing); + + this.outgoing = []; + }, + 100, + { + maxWait: 300, + } + ); + + handleIncomingRequest = (request: TRPCClientIncomingRequest) => { + // TODO: handle incoming request + throw new Error("Not implemented"); + }; + handleIncomingResponse = (response: TRPCResponseMessage) => { + const resolve = this.pendingRequests.get(response.id); + if (!resolve) { + return; + } + + resolve(response); + }; + subscribe = () => { + const listener = (event: MessageEvent) => { + const parsed = z + .object({ + source: z.literal("dappstore-sdk-router"), + target: z.string(), + messages: z.array(z.unknown()), + }) + .safeParse(event.data); + + if (!parsed.success || parsed.data.target !== this.id) { + return; + } + + parsed.data.messages.forEach((message: any) => { + if ("method" in message) { + this.handleIncomingRequest(message); + } else { + this.handleIncomingResponse(message); + } + }); + }; + if (typeof window === "undefined") return () => {}; + + window.addEventListener("message", listener, false); + return () => { + window.removeEventListener("message", listener); + }; + }; +} + +export const postMessageLink = ( + opts: PostMessageOptions = {} +): TRPCLink => { + const transformer = getTransformer(opts.transformer); + const client = new PostMessageClient(); + client.subscribe(); + return () => { + return ({ op }) => { + const { type, path, id, context } = op; + + const input = transformer.input.serialize(op.input); + + return observable((observer) => { + const unsubscribe = client.request( + { + type, + path, + input, + id, + context, + }, + (data) => { + const transformed = transformResult( + data, + transformer.output + ); + + if (!transformed.ok) { + observer.error(TRPCClientError.from(transformed.error)); + return; + } + observer.next(transformed); + if (op.type === "subscription") return; + observer.complete(); + unsubscribe(); + }, + observer + ); + return unsubscribe; + }); + }; + }; +}; diff --git a/packages/dappstore-sdk/src/trpc/client.ts b/packages/dappstore-sdk/src/trpc/client.ts new file mode 100644 index 0000000..0ab9795 --- /dev/null +++ b/packages/dappstore-sdk/src/trpc/client.ts @@ -0,0 +1,10 @@ +import { createTRPCClient } from "@trpc/client"; +import type { CreateTRPCClient } from "@trpc/client"; + +import type { HostRouter } from "./host.js"; + +import { postMessageLink } from "../post-message-integration/post-message-link.js"; +export const trpcClient: CreateTRPCClient = + createTRPCClient({ + links: [postMessageLink({})], + }); diff --git a/packages/dappstore-sdk/src/trpc/host.ts b/packages/dappstore-sdk/src/trpc/host.ts new file mode 100644 index 0000000..21e3175 --- /dev/null +++ b/packages/dappstore-sdk/src/trpc/host.ts @@ -0,0 +1,77 @@ +import { initTRPC } from "@trpc/server"; +import { z } from "zod"; +import { observable } from "@trpc/server/observable"; +import { EIP1193Provider, Hex, ProviderConnectInfo } from "viem"; + +const t = initTRPC.create({ + allowOutsideOfServer: true, +}); + +export const createHostRouter = (provider: unknown) => { + const _provider: EIP1193Provider = provider as EIP1193Provider; + const providerRouter = t.router({ + on: t.router({ + accountsChanged: t.procedure.subscription((a) => { + return observable((emit) => { + _provider.on("accountsChanged", (accounts) => { + emit.next(accounts); + }); + }); + }), + + chainChanged: t.procedure.subscription((a) => { + return observable((emit) => { + _provider.on("chainChanged", (chainId) => { + emit.next(chainId as Hex); + }); + }); + }), + connect: t.procedure.subscription((a) => { + return observable((emit) => { + _provider.on("connect", (...args) => { + emit.next(...args); + }); + }); + }), + }), + + request: t.procedure + .input( + z + .object({ + method: z.string(), + params: z.array(z.unknown()).or(z.object({})).optional(), + }) + .readonly() + ) + .mutation((opts) => { + return _provider.request(opts.input as never); + }), + }); + + return t.router({ + ack: t.procedure + .input( + z.object({ + version: z.string(), + }) + ) + .query(async () => { + const accounts = await _provider.request({ + method: "eth_requestAccounts", + }); + const chainId = await _provider.request({ method: "eth_chainId" }); + return { + version: process.env.npm_package_version as string, + state: { + accounts, + chainId, + }, + }; + }), + + provider: providerRouter, + }); +}; + +export type HostRouter = ReturnType; diff --git a/packages/dappstore-sdk/src/types/EIP1193Provider.ts b/packages/dappstore-sdk/src/types/EIP1193Provider.ts new file mode 100644 index 0000000..a50281d --- /dev/null +++ b/packages/dappstore-sdk/src/types/EIP1193Provider.ts @@ -0,0 +1,45 @@ +export interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} +export interface ProviderConnectInfo { + readonly chainId: string; +} +export interface ProviderRpcError extends Error { + message: string; + code: number; + data?: unknown; +} +export interface ProviderMessage { + readonly type: string; + readonly data: unknown; +} +export type ProviderChainChangedListener = (chainId: string) => void; +export type ProviderAccountsChangedListener = (accounts: string[]) => void; +export type ProviderConnectListener = ( + connectInfo: ProviderConnectInfo +) => void; +export type ProviderDisconnectListener = (error: ProviderRpcError) => void; +export type ProviderMessageListener = (message: ProviderMessage) => void; +export interface EIP1193Provider { + on( + event: "accountsChanged", + listener: ProviderAccountsChangedListener + ): EIP1193Provider; + on( + event: "chainChanged", + listener: ProviderChainChangedListener + ): EIP1193Provider; + on(event: "connect", listener: ProviderConnectListener): EIP1193Provider; + on( + event: "disconnect", + listener: ProviderDisconnectListener + ): EIP1193Provider; + on(event: "message", listener: ProviderMessageListener): EIP1193Provider; + + removeListener( + event: string, + callback: (data: unknown) => void + ): EIP1193Provider; + request(args: RequestArguments): Promise; +} diff --git a/packages/dappstore-sdk/tsconfig.json b/packages/dappstore-sdk/tsconfig.json new file mode 100644 index 0000000..66bd5b8 --- /dev/null +++ b/packages/dappstore-sdk/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@evmos/config/base.json", + "compilerOptions": { + "outDir": "./dist", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["./src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/dev-wrapper/.eslintrc.cjs b/packages/dev-wrapper/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/packages/dev-wrapper/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/packages/dev-wrapper/.gitignore b/packages/dev-wrapper/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/packages/dev-wrapper/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/dev-wrapper/README.md b/packages/dev-wrapper/README.md new file mode 100644 index 0000000..0d6babe --- /dev/null +++ b/packages/dev-wrapper/README.md @@ -0,0 +1,30 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/packages/dev-wrapper/index.html b/packages/dev-wrapper/index.html new file mode 100644 index 0000000..191e59e --- /dev/null +++ b/packages/dev-wrapper/index.html @@ -0,0 +1,14 @@ + + + + + + + DAppStore Widget Development Kit + + +
+ {{ENV_SCRIPT}} + + + diff --git a/packages/dev-wrapper/package.json b/packages/dev-wrapper/package.json new file mode 100644 index 0000000..782b262 --- /dev/null +++ b/packages/dev-wrapper/package.json @@ -0,0 +1,34 @@ +{ + "name": "@evmos/dev-wrapper", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + "./*": "./dist/*" + }, + "scripts": { + "dev": "tsc --watch & tsc -p ./tsconfig.serve.json --watch & vite build --watch --outDir ./dist/app", + "build": "tsc && tsc -p ./tsconfig.serve.json && vite build --outDir ./dist/app", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "clsx": "^2.1.0", + "express": "^4.18.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@evmos/dappstore-sdk": "workspace:*", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.56.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "typescript": "^5.2.2", + "vite": "^5.1.0" + } +} diff --git a/packages/dev-wrapper/postcss.config.js b/packages/dev-wrapper/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/packages/dev-wrapper/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/dev-wrapper/public/vite.svg b/packages/dev-wrapper/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/packages/dev-wrapper/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/dev-wrapper/serve.ts b/packages/dev-wrapper/serve.ts new file mode 100644 index 0000000..a16bbe8 --- /dev/null +++ b/packages/dev-wrapper/serve.ts @@ -0,0 +1,27 @@ +import { fileURLToPath } from "url"; +import express from "express"; +import path from "path"; +import { readFileSync } from "fs"; +const __dirname = fileURLToPath(new URL("./", import.meta.url)); + +export const serve = ({ target }: { target: string }) => { + const targetUrl = target.startsWith("http") + ? target + : `http://localhost:${target}`; + + const app = express(); + const envScript = ``; + const index = readFileSync( + path.join(__dirname, "../app/index.html"), + "utf-8" + ).replace("{{ENV_SCRIPT}}", envScript); + + app.get("/", (req, res) => { + if (req.originalUrl === "/") { + res.end(index); + } + }); + app.use(express.static(path.join(__dirname, "../app"))); + + return app; +}; diff --git a/packages/dev-wrapper/src/App.css b/packages/dev-wrapper/src/App.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/packages/dev-wrapper/src/App.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/dev-wrapper/src/App.tsx b/packages/dev-wrapper/src/App.tsx new file mode 100644 index 0000000..c367163 --- /dev/null +++ b/packages/dev-wrapper/src/App.tsx @@ -0,0 +1,171 @@ +import React, { useEffect, useState } from "react"; +import { WagmiProvider } from "wagmi"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useConnect, useChainId, useSwitchChain } from "wagmi"; +import { useDisconnect } from "wagmi"; +import { useAccount, useConnections } from "wagmi"; +import { type EIP1193Provider } from "viem"; +import { EvmosLogo } from "./EvmosLogo.js"; +import { config } from "./config.js"; + +import { createHost } from "@evmos/dappstore-sdk/internal/host"; + +import cx from "clsx"; + +const Header = () => { + return ( +
+
+ +
+
+ + + +
+
+ ); +}; +export function WalletOptions() { + const { connectors, connect, isPending } = useConnect(); + const { disconnect } = useDisconnect(); + const { isConnected, address } = useAccount(); + const shortenedAddress = address + ? `${address.slice(0, 6)}...${address.slice(-4)}` + : ""; + if (isConnected) { + return ( + + ); + } + + return connectors.map((connector) => ( + + )); +} + +function Networks() { + // const { address, isConnecting } = useAccount(); + // const {} = useConfig(); + const chainId = useChainId(); + const { chains, switchChain, isPending } = useSwitchChain(); + + return ( +
+ {chains.map((chain) => ( + + ))} + + Testnet Faucet + + + + +
+ ); +} + +const WidgetArea = () => { + const connections = useConnections(); + const ref = React.useRef(null); + const [provider, setProvider] = useState(null); + const [url] = useState(() => { + if (typeof window === "undefined") { + return ""; + } + if (!("__ENV__" in window)) { + return ""; + } + return (window.__ENV__ as Record).TARGET || ""; + }); + useEffect(() => { + const connection = connections[0]; + if (!connection) { + setProvider(null); + return; + } + if (!("getProvider" in connection.connector)) { + setProvider(null); + return; + } + connection.connector.getProvider().then((p) => { + console.log(p); + setProvider(p as EIP1193Provider); + }); + }, [connections]); + + useEffect(() => { + if (!provider || !ref.current) { + return; + } + return createHost({ + provider, + target: ref.current.contentWindow as Window, + }); + }, [ref, provider]); + + if (!provider) { + return
Connect to a wallet
; + } + + return