From b482b92e4f6cf870ad85a5d95a2a31983f14f34d Mon Sep 17 00:00:00 2001 From: Ilango Date: Thu, 11 Jul 2024 19:17:24 +0530 Subject: [PATCH 1/2] Fix remix example and use latest remix+vite stable versions --- examples/remix/.eslintrc.cjs | 84 ++++++++++++++++ examples/remix/README.md | 45 +++++---- examples/remix/app/entry.client.tsx | 18 ++++ examples/remix/app/entry.server.tsx | 140 +++++++++++++++++++++++++++ examples/remix/app/root.tsx | 11 ++- examples/remix/app/routes/_index.tsx | 23 ++--- examples/remix/env.d.ts | 2 - examples/remix/package.json | 56 ++++++----- examples/remix/postcss.config.js | 2 +- examples/remix/server.mjs | 49 ---------- examples/remix/tailwind.config.ts | 19 +--- examples/remix/tsconfig.json | 13 ++- examples/remix/vite.config.ts | 34 +++---- 13 files changed, 344 insertions(+), 152 deletions(-) create mode 100644 examples/remix/.eslintrc.cjs create mode 100644 examples/remix/app/entry.client.tsx create mode 100644 examples/remix/app/entry.server.tsx delete mode 100644 examples/remix/env.d.ts delete mode 100644 examples/remix/server.mjs diff --git a/examples/remix/.eslintrc.cjs b/examples/remix/.eslintrc.cjs new file mode 100644 index 000000000..4f6f59eee --- /dev/null +++ b/examples/remix/.eslintrc.cjs @@ -0,0 +1,84 @@ +/** + * This is intended to be a basic starting point for linting in your app. + * It relies on recommended configs out of the box for simplicity, but you can + * and should modify this configuration to best suit your team's needs. + */ + +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser: true, + commonjs: true, + es6: true, + }, + ignorePatterns: ["!**/.server", "!**/.client"], + + // Base config + extends: ["eslint:recommended"], + + overrides: [ + // React + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: ["react", "jsx-a11y"], + extends: [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended", + ], + settings: { + react: { + version: "detect", + }, + formComponents: ["Form"], + linkComponents: [ + { name: "Link", linkAttribute: "to" }, + { name: "NavLink", linkAttribute: "to" }, + ], + "import/resolver": { + typescript: {}, + }, + }, + }, + + // Typescript + { + files: ["**/*.{ts,tsx}"], + plugins: ["@typescript-eslint", "import"], + parser: "@typescript-eslint/parser", + settings: { + "import/internal-regex": "^~/", + "import/resolver": { + node: { + extensions: [".ts", ".tsx"], + }, + typescript: { + alwaysTryTypes: true, + }, + }, + }, + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + ], + }, + + // Node + { + files: [".eslintrc.cjs"], + env: { + node: true, + }, + }, + ], +}; diff --git a/examples/remix/README.md b/examples/remix/README.md index 994d6be92..6c4d2168f 100644 --- a/examples/remix/README.md +++ b/examples/remix/README.md @@ -1,41 +1,40 @@ -# templates/unstable-vite-express +# Welcome to Remix! -⚠️ Remix support for Vite is unstable and not recommended for production. +- 📖 [Remix docs](https://remix.run/docs) -📖 See the [Remix Vite docs][remix-vite-docs] for details on supported features. +## Development -## Setup +Run the dev server: ```shellscript -npx create-remix@latest --template remix-run/remix/templates/unstable-vite-express +npm run dev ``` -## Run +## Deployment -Spin up the Express server as a dev server: +First, build your app for production: -```shellscript -npm run dev +```sh +npm run build ``` -Or build your app for production and run it: +Then run the app in production mode: -```shellscript -npm run build -npm run start +```sh +npm start ``` -## Customize +Now you'll need to pick a host to deploy it to. -Remix exposes APIs for integrating Vite with a custom server: +### DIY -```ts -import { - unstable_createViteServer, - unstable_loadViteServerBuild, -} from "@remix-run/dev"; -``` +If you're familiar with deploying Node applications, the built-in Remix app server is production-ready. + +Make sure to deploy the output of `npm run build` + +- `build/server` +- `build/client` -In this template, we'll use Express but remember that these APIs can be used with _any_ Node-compatible server setup that supports standard middleware. +## Styling -[remix-vite-docs]: https://remix.run/docs/en/main/future/vite +This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever css framework you prefer. See the [Vite docs on css](https://vitejs.dev/guide/features.html#css) for more information. diff --git a/examples/remix/app/entry.client.tsx b/examples/remix/app/entry.client.tsx new file mode 100644 index 000000000..94d5dc0de --- /dev/null +++ b/examples/remix/app/entry.client.tsx @@ -0,0 +1,18 @@ +/** + * By default, Remix will handle hydrating your app on the client for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.client + */ + +import { RemixBrowser } from "@remix-run/react"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; + +startTransition(() => { + hydrateRoot( + document, + + + + ); +}); diff --git a/examples/remix/app/entry.server.tsx b/examples/remix/app/entry.server.tsx new file mode 100644 index 000000000..45db3229c --- /dev/null +++ b/examples/remix/app/entry.server.tsx @@ -0,0 +1,140 @@ +/** + * By default, Remix will handle generating the HTTP Response for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.server + */ + +import { PassThrough } from "node:stream"; + +import type { AppLoadContext, EntryContext } from "@remix-run/node"; +import { createReadableStreamFromReadable } from "@remix-run/node"; +import { RemixServer } from "@remix-run/react"; +import { isbot } from "isbot"; +import { renderToPipeableStream } from "react-dom/server"; + +const ABORT_DELAY = 5_000; + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, + // This is ignored so we can keep it in the template for visibility. Feel + // free to delete this parameter in your app if you're not using it! + // eslint-disable-next-line @typescript-eslint/no-unused-vars + loadContext: AppLoadContext +) { + return isbot(request.headers.get("user-agent") || "") + ? handleBotRequest( + request, + responseStatusCode, + responseHeaders, + remixContext + ) + : handleBrowserRequest( + request, + responseStatusCode, + responseHeaders, + remixContext + ); +} + +function handleBotRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext +) { + return new Promise((resolve, reject) => { + let shellRendered = false; + const { pipe, abort } = renderToPipeableStream( + , + { + onAllReady() { + shellRendered = true; + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); + + responseHeaders.set("Content-Type", "text/html"); + + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }) + ); + + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) { + console.error(error); + } + }, + } + ); + + setTimeout(abort, ABORT_DELAY); + }); +} + +function handleBrowserRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext +) { + return new Promise((resolve, reject) => { + let shellRendered = false; + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + shellRendered = true; + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); + + responseHeaders.set("Content-Type", "text/html"); + + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }) + ); + + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) { + console.error(error); + } + }, + } + ); + + setTimeout(abort, ABORT_DELAY); + }); +} diff --git a/examples/remix/app/root.tsx b/examples/remix/app/root.tsx index fec2b8c94..d524baeef 100644 --- a/examples/remix/app/root.tsx +++ b/examples/remix/app/root.tsx @@ -1,15 +1,13 @@ import { Links, - LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; - import "./tailwind.css"; -export default function App() { +export function Layout({ children }: { children: React.ReactNode }) { return ( @@ -19,11 +17,14 @@ export default function App() { - + {children} - ); } + +export default function App() { + return ; +} diff --git a/examples/remix/app/routes/_index.tsx b/examples/remix/app/routes/_index.tsx index b1c4b6cb0..491d42b9e 100644 --- a/examples/remix/app/routes/_index.tsx +++ b/examples/remix/app/routes/_index.tsx @@ -1,21 +1,22 @@ import * as S from "@effect/schema/Schema"; import { formatError } from "@effect/schema/TreeFormatter"; import { - EvoluProvider, ExtractRow, NonEmptyString1000, - NotNull, SqliteBoolean, String, - canUseDom, cast, - createEvolu, createIndexes, database, id, - jsonArrayFrom, parseMnemonic, table, +} from "@evolu/common"; +import { createEvolu } from "@evolu/common-web"; +import { + EvoluProvider, + NotNull, + jsonArrayFrom, useEvolu, useEvoluError, useOwner, @@ -47,7 +48,7 @@ type TodoCategoryId = typeof TodoCategoryId.Type; const NonEmptyString50 = String.pipe( S.minLength(1), S.maxLength(50), - S.brand("NonEmptyString50"), + S.brand("NonEmptyString50") ); type NonEmptyString50 = typeof NonEmptyString50.Type; @@ -178,13 +179,13 @@ const todosWithCategories = evolu.createQuery( .selectFrom("todoCategory") .select(["todoCategory.id", "todoCategory.name"]) .where("isDeleted", "is not", cast(true)) - .orderBy("createdAt"), + .orderBy("createdAt") ).as("categories"), ]), { // logQueryExecutionTime: true, // logExplainQueryPlan: true, - }, + } ); type TodosWithCategoriesRow = ExtractRow; @@ -293,7 +294,7 @@ const todoCategories = evolu.createQuery((db) => // Filter null value and ensure non-null type. .where("name", "is not", null) .$narrowType<{ name: NotNull }>() - .orderBy("createdAt"), + .orderBy("createdAt") ); type TodoCategoriesRow = ExtractRow; @@ -369,7 +370,7 @@ const OwnerActions: FC = () => { onSuccess: (mnemonic) => { evolu.restoreOwner(mnemonic); }, - }), + }) ); }); }; @@ -423,7 +424,7 @@ const Button: FC<{ const prompt = ( schema: S.Schema, message: string, - onSuccess: (value: To) => void, + onSuccess: (value: To) => void ) => { const value = window.prompt(message); if (value == null) return; // on cancel diff --git a/examples/remix/env.d.ts b/examples/remix/env.d.ts deleted file mode 100644 index 78ed2345c..000000000 --- a/examples/remix/env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// diff --git a/examples/remix/package.json b/examples/remix/package.json index fa6acd77c..eb3c67e50 100644 --- a/examples/remix/package.json +++ b/examples/remix/package.json @@ -4,38 +4,46 @@ "sideEffects": false, "type": "module", "scripts": { - "build": "vite build && vite build --ssr", - "dev": "node ./server.mjs", - "start": "cross-env NODE_ENV=production node ./server.mjs", + "build": "remix vite:build", + "dev": "remix vite:dev", + "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", + "start": "remix-serve ./build/server/index.js", "typecheck": "tsc" }, "dependencies": { - "@evolu/react": "latest", - "@evolu/server": "latest", - "@remix-run/dev": "^2.3.1", - "@remix-run/express": "^2.3.1", - "@remix-run/node": "^2.3.1", - "@remix-run/react": "^2.3.1", - "express": "^4.18.2", - "isbot": "^3.6.8", - "react": "18.2.0", - "react-dom": "18.2.0" + "@effect/schema": "^0.68.17", + "@evolu/common": "5.2.3", + "@evolu/common-web": "8.1.1", + "@evolu/react": "8.1.0", + "@evolu/server": "7.0.13", + "@remix-run/node": "^2.10.2", + "@remix-run/react": "^2.10.2", + "@remix-run/serve": "^2.10.2", + "effect": "^3.4.7", + "isbot": "^4.1.0", + "react": "18.3.1", + "react-dom": "18.3.1" }, "devDependencies": { - "@remix-run/eslint-config": "^2.3.1", - "@types/express": "^4.17.20", - "@types/react": "~18.3.1", - "@types/react-dom": "~18.2.25", - "autoprefixer": "^10.4.16", - "cross-env": "^7.0.3", + "@remix-run/dev": "^2.10.2", + "@types/react": "^18.2.20", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", + "autoprefixer": "^10.4.19", "eslint": "^8.38.0", - "postcss": "^8.4.32", - "tailwindcss": "^3.3.5", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.4", "typescript": "^5.1.6", - "vite": "^5.0.0", + "vite": "^5.1.0", "vite-tsconfig-paths": "^4.2.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } -} +} \ No newline at end of file diff --git a/examples/remix/postcss.config.js b/examples/remix/postcss.config.js index 2e7af2b7f..2aa7205d4 100644 --- a/examples/remix/postcss.config.js +++ b/examples/remix/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/examples/remix/server.mjs b/examples/remix/server.mjs deleted file mode 100644 index 8cca0adee..000000000 --- a/examples/remix/server.mjs +++ /dev/null @@ -1,49 +0,0 @@ -import { - unstable_createViteServer, - unstable_loadViteServerBuild, -} from "@remix-run/dev"; -import { createRequestHandler } from "@remix-run/express"; -import { installGlobals } from "@remix-run/node"; -import express from "express"; -import { createExpressApp } from "@evolu/server"; -import { Effect } from "effect"; - -installGlobals(); - -let vite = - process.env.NODE_ENV === "production" - ? undefined - : await unstable_createViteServer(); - -const app = express(); - -// uncomment this line if you want your server to be used -// for evolu sync instead of the default https://evolu.world/ -// const app = await Effect.runPromise(createExpressApp); - -// handle asset requests -if (vite) { - app.use(vite.middlewares); -} else { - app.use( - "/build", - express.static("public/build", { - immutable: true, - maxAge: "1y", - }), - ); -} -app.use(express.static("public", { maxAge: "1h" })); - -// handle SSR requests -app.all( - "*", - createRequestHandler({ - build: vite - ? () => unstable_loadViteServerBuild(vite) - : await import("./build/index.js"), - }), -); - -const port = 3000; -app.listen(port, () => console.log("http://localhost:" + port)); diff --git a/examples/remix/tailwind.config.ts b/examples/remix/tailwind.config.ts index ab96d38d3..64a5243e4 100644 --- a/examples/remix/tailwind.config.ts +++ b/examples/remix/tailwind.config.ts @@ -1,18 +1,9 @@ -/** @type {import("tailwindcss").Config} */ +import type { Config } from "tailwindcss"; + export default { - content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", - ], + content: ["./app/**/*.{js,jsx,ts,tsx}"], theme: { - extend: { - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", - }, - }, + extend: {}, }, plugins: [], -}; +} satisfies Config; diff --git a/examples/remix/tsconfig.json b/examples/remix/tsconfig.json index 269c0cc0f..9d87dd378 100644 --- a/examples/remix/tsconfig.json +++ b/examples/remix/tsconfig.json @@ -1,7 +1,15 @@ { - "include": ["env.d.ts", "**/*.ts", "**/*.tsx"], + "include": [ + "**/*.ts", + "**/*.tsx", + "**/.server/**/*.ts", + "**/.server/**/*.tsx", + "**/.client/**/*.ts", + "**/.client/**/*.tsx" + ], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["@remix-run/node", "vite/client"], "isolatedModules": true, "esModuleInterop": true, "jsx": "react-jsx", @@ -11,13 +19,14 @@ "target": "ES2022", "strict": true, "allowJs": true, + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "~/*": ["./app/*"] }, - // Remix takes care of building everything in `remix build`. + // Vite takes care of building everything, not tsc. "noEmit": true } } diff --git a/examples/remix/vite.config.ts b/examples/remix/vite.config.ts index 28585c55b..cc88233f3 100644 --- a/examples/remix/vite.config.ts +++ b/examples/remix/vite.config.ts @@ -1,28 +1,20 @@ -import { unstable_vitePlugin as remix } from "@remix-run/dev"; +import { vitePlugin as remix } from "@remix-run/dev"; import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ - optimizeDeps: { - // A workaround for Vite bug: https://github.com/vitejs/vite/issues/13314#issuecomment-1560745780 - exclude: ["@evolu/react"], - // Another workaround for Vite bug: https://github.com/radix-ui/primitives/discussions/1915#discussioncomment-5733178 - include: ["react-dom"], - }, - - build: { - rollupOptions: { - output: { - entryFileNames: `[name][hash].js`, - chunkFileNames: `[name][hash].js`, - assetFileNames: `[name][hash].[ext]`, + plugins: [ + remix({ + future: { + v3_fetcherPersist: true, + v3_relativeSplatPath: true, + v3_throwAbortReason: true, }, - }, - }, - - worker: { - format: "es", + }), + tsconfigPaths(), + ], + optimizeDeps: { + exclude: ["@evolu/common-web"], }, - - plugins: [remix(), tsconfigPaths()], + worker: { format: "es" }, }); From a779c8efc8e7bdd72d53a6b83988bdfa2fc6b3c2 Mon Sep 17 00:00:00 2001 From: Ilango Date: Thu, 11 Jul 2024 19:46:03 +0530 Subject: [PATCH 2/2] Fix vite+react+ts example --- examples/vite/README.md | 27 ++++++++++++ examples/vite/index.html | 3 +- examples/vite/package.json | 32 ++++++++------ examples/vite/src/App.css | 42 ++++++++++++++++++ examples/vite/src/App.tsx | 11 ++--- examples/vite/src/assets/react.svg | 1 + examples/vite/src/index.css | 68 ++++++++++++++++++++++++++++++ examples/vite/src/main.tsx | 11 ++--- examples/vite/tsconfig.app.json | 27 ++++++++++++ examples/vite/tsconfig.json | 32 ++++---------- examples/vite/tsconfig.node.json | 5 ++- examples/vite/vite.config.ts | 26 ++---------- 12 files changed, 214 insertions(+), 71 deletions(-) create mode 100644 examples/vite/src/App.css create mode 100644 examples/vite/src/assets/react.svg create mode 100644 examples/vite/src/index.css create mode 100644 examples/vite/tsconfig.app.json diff --git a/examples/vite/README.md b/examples/vite/README.md index 3b04c2577..0d6babedd 100644 --- a/examples/vite/README.md +++ b/examples/vite/README.md @@ -1,3 +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/vite/index.html b/examples/vite/index.html index 7e6449b63..e4b78eae1 100644 --- a/examples/vite/index.html +++ b/examples/vite/index.html @@ -1,7 +1,8 @@ - + + Vite + React + TS diff --git a/examples/vite/package.json b/examples/vite/package.json index b0e1de9ff..452364a0f 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -1,29 +1,33 @@ { - "name": "vite-project", + "name": "vite-evolu", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "tsc -b && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { - "@evolu/react": "latest", - "react": "18.2.0", - "react-dom": "18.2.0" + "@effect/schema": "^0.68.21", + "@evolu/common": "^5.2.3", + "@evolu/common-web": "^8.1.1", + "@evolu/react": "^8.1.0", + "effect": "^3.5.1", + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@types/react": "~18.2.45", - "@types/react-dom": "~18.2.25", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "@vitejs/plugin-react": "^4.2.0", - "eslint": "^8.53.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.4", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", "typescript": "^5.2.2", - "vite": "^5.0.0" + "vite": "^5.3.1" } } diff --git a/examples/vite/src/App.css b/examples/vite/src/App.css new file mode 100644 index 000000000..b9d355df2 --- /dev/null +++ b/examples/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/vite/src/App.tsx b/examples/vite/src/App.tsx index 54f89d3f9..b74acce56 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,21 +1,22 @@ import * as S from "@effect/schema/Schema"; import { formatError } from "@effect/schema/TreeFormatter"; import { - EvoluProvider, ExtractRow, NonEmptyString1000, - NotNull, SqliteBoolean, String, - canUseDom, cast, - createEvolu, createIndexes, database, id, - jsonArrayFrom, parseMnemonic, table, +} from "@evolu/common"; +import { createEvolu } from "@evolu/common-web"; +import { + EvoluProvider, + NotNull, + jsonArrayFrom, useEvolu, useEvoluError, useOwner, diff --git a/examples/vite/src/assets/react.svg b/examples/vite/src/assets/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/examples/vite/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/vite/src/index.css b/examples/vite/src/index.css new file mode 100644 index 000000000..6119ad9a8 --- /dev/null +++ b/examples/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/vite/src/main.tsx b/examples/vite/src/main.tsx index b5c00920e..3d7150da8 100644 --- a/examples/vite/src/main.tsx +++ b/examples/vite/src/main.tsx @@ -1,9 +1,10 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' -ReactDOM.createRoot(document.getElementById("root")!).render( +ReactDOM.createRoot(document.getElementById('root')!).render( , -); +) diff --git a/examples/vite/tsconfig.app.json b/examples/vite/tsconfig.app.json new file mode 100644 index 000000000..d739292ae --- /dev/null +++ b/examples/vite/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/vite/tsconfig.json b/examples/vite/tsconfig.json index a7fc6fbf2..ea9d0cd82 100644 --- a/examples/vite/tsconfig.json +++ b/examples/vite/tsconfig.json @@ -1,25 +1,11 @@ { - "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" }] + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/examples/vite/tsconfig.node.json b/examples/vite/tsconfig.node.json index 42872c59f..3afdd6e38 100644 --- a/examples/vite/tsconfig.node.json +++ b/examples/vite/tsconfig.node.json @@ -1,10 +1,13 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/examples/vite/vite.config.ts b/examples/vite/vite.config.ts index c9ff94edd..b6194e6e7 100644 --- a/examples/vite/vite.config.ts +++ b/examples/vite/vite.config.ts @@ -1,29 +1,11 @@ -import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ + plugins: [react()], optimizeDeps: { - exclude: [ - // Do not pre-bundle `@evolu/common-web`. Why? Worker relative import path resolution fails on registration because `@evolu/common-web/dist/*.worker.js` - // is not present in Vite's pre-bundled dependencies cache (typically `node_modules/.vite/deps`). - // @see https://github.com/vitejs/vite/issues/13314#issuecomment-1560745780 - "@evolu/common-web", - ], - // Another workaround for Vite bug: https://github.com/radix-ui/primitives/discussions/1915#discussioncomment-5733178 - include: ["react-dom"], + exclude: ["@evolu/common-web"] }, - build: { - rollupOptions: { - output: { - entryFileNames: `[name][hash].js`, - chunkFileNames: `[name][hash].js`, - assetFileNames: `[name][hash].[ext]`, - }, - }, - }, - worker: { - format: "es", - }, - plugins: [react()], + worker: { format: "es" } });