diff --git a/.envrc b/.envrc index 8106bf9..117c726 100644 --- a/.envrc +++ b/.envrc @@ -1,13 +1,17 @@ -if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +#!/usr/bin/env bash + +if ! has nix_direnv_version || ! nix_direnv_version 3.1.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.0/direnvrc" "sha256-yMJ2OVMzrFaDPn7q8nCBZFRYpL/f0RcHzhmw/i6btJM=" fi +export DEVENV_IN_DIRENV_SHELL=true + watch_file flake.nix watch_file flake.lock -DEVENV_ROOT_FILE="$(mktemp)" -printf %s "$PWD" > "$DEVENV_ROOT_FILE" -if ! use flake . --override-input devenv-root "file+file://$DEVENV_ROOT_FILE" -then +mkdir -p "$PWD/.devenv" +DEVENV_ROOT_FILE="$PWD/.devenv/root" +printf %s "$PWD" >"$DEVENV_ROOT_FILE" +if ! use flake . --override-input devenv-root "file+file://$DEVENV_ROOT_FILE"; then echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 fi diff --git a/.gitignore b/.gitignore index d2fcaf2..acf6b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,10 @@ node_modules # testing /coverage -# production -/build +# React Router +.react-router/ +/build/ + # misc .DS_Store diff --git a/biome.json b/biome.json index 7cdcf69..1498ed4 100644 --- a/biome.json +++ b/biome.json @@ -1,7 +1,13 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", - "organizeImports": { - "enabled": true + "$schema": "https://biomejs.dev/schemas/2.3.6/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": ["**", "!**/.react-router"] }, "formatter": { "enabled": true, @@ -20,9 +26,17 @@ } } }, - "vcs": { + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { "enabled": true, - "clientKind": "git", - "useIgnoreFile": true + "actions": { + "source": { + "organizeImports": "on" + } + } } } diff --git a/bun.lockb b/bun.lockb index d0d030b..5faa1c3 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/flake.nix b/flake.nix index 8d7fa81..2525c5e 100644 --- a/flake.nix +++ b/flake.nix @@ -6,8 +6,9 @@ url = "file+file:///dev/null"; flake = false; }; + nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling"; flake-parts.url = "github:hercules-ci/flake-parts"; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; devenv.url = "github:cachix/devenv"; nix2container.url = "github:nlewo/nix2container"; nix2container.inputs.nixpkgs.follows = "nixpkgs"; @@ -19,54 +20,40 @@ extra-substituters = "https://devenv.cachix.org"; }; - outputs = inputs @ { - flake-parts, - devenv-root, - ... - }: - flake-parts.lib.mkFlake {inherit inputs;} { + outputs = + inputs@{ flake-parts, devenv-root, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { imports = [ inputs.devenv.flakeModule ]; - systems = ["x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"]; + systems = [ + "x86_64-linux" + "i686-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; - perSystem = { - #config, - #self', - ##inputs', - pkgs, - #system, - ... - }: let - in { - devenv.shells.default = { - devenv.root = let - devenvRootFileContent = builtins.readFile devenv-root.outPath; - in - pkgs.lib.mkIf (devenvRootFileContent != "") devenvRootFileContent; + perSystem = + { + config, + self', + inputs', + pkgs, + system, + ... + }: + { + devenv.shells.default = { + name = "my-project"; + imports = [ ]; - name = "TODO-template-name"; - - packages = [ - pkgs.biome - ]; - - processes = { - #api-dev.exec = "cd packages/api && bun dev"; - #web-dev.exec = "cd packages/web && bun dev"; - }; - - languages.javascript = { - enable = true; - package = pkgs.nodejs_22; - - bun.enable = true; - npm.enable = false; - pnpm.enable = false; + languages.javascript = { + enable = true; + bun.enable = true; + }; }; }; - }; - flake = { - }; + flake = { }; }; } diff --git a/packages/web/app/app.css b/packages/web/app/app.css new file mode 100644 index 0000000..71277f5 --- /dev/null +++ b/packages/web/app/app.css @@ -0,0 +1,4 @@ +html, +body { + background-color: teal; +} diff --git a/packages/web/app/root.tsx b/packages/web/app/root.tsx new file mode 100644 index 0000000..9fc6636 --- /dev/null +++ b/packages/web/app/root.tsx @@ -0,0 +1,75 @@ +import { + isRouteErrorResponse, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "react-router"; + +import type { Route } from "./+types/root"; +import "./app.css"; + +export const links: Route.LinksFunction = () => [ + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", + }, +]; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ); +} + +export default function App() { + return ; +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = "Oops!"; + let details = "An unexpected error occurred."; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? "404" : "Error"; + details = + error.status === 404 + ? "The requested page could not be found." + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} diff --git a/packages/web/app/routes.ts b/packages/web/app/routes.ts new file mode 100644 index 0000000..102b402 --- /dev/null +++ b/packages/web/app/routes.ts @@ -0,0 +1,3 @@ +import { type RouteConfig, index } from "@react-router/dev/routes"; + +export default [index("routes/home.tsx")] satisfies RouteConfig; diff --git a/packages/web/app/routes/home.tsx b/packages/web/app/routes/home.tsx new file mode 100644 index 0000000..28efe48 --- /dev/null +++ b/packages/web/app/routes/home.tsx @@ -0,0 +1,13 @@ +import type { Route } from "./+types/home"; +import { Welcome } from "../welcome/welcome"; + +export function meta(_args: Route.MetaArgs) { + return [ + { title: "New React Router App" }, + { name: "description", content: "Welcome to React Router!" }, + ]; +} + +export default function Home() { + return ; +} diff --git a/packages/web/app/welcome/logo-dark.svg b/packages/web/app/welcome/logo-dark.svg new file mode 100644 index 0000000..dd82028 --- /dev/null +++ b/packages/web/app/welcome/logo-dark.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web/app/welcome/logo-light.svg b/packages/web/app/welcome/logo-light.svg new file mode 100644 index 0000000..7328492 --- /dev/null +++ b/packages/web/app/welcome/logo-light.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web/app/welcome/welcome.tsx b/packages/web/app/welcome/welcome.tsx new file mode 100644 index 0000000..f4c185f --- /dev/null +++ b/packages/web/app/welcome/welcome.tsx @@ -0,0 +1,91 @@ +import logoDark from "./logo-dark.svg"; +import logoLight from "./logo-light.svg"; + +export function Welcome() { + return ( +
+
+
+
+ React Router + React Router +
+
+
+ +
+
+
+ ); +} + +const resources = [ + { + href: "https://reactrouter.com/docs", + text: "React Router Docs", + icon: ( + + Welcome + + + ), + }, + { + href: "https://rmx.as/discord", + text: "Join Discord", + icon: ( + + Welcome + + + ), + }, +]; diff --git a/packages/web/index.html b/packages/web/index.html deleted file mode 100644 index 2ce0cce..0000000 --- a/packages/web/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - Template - - -
- - - diff --git a/packages/web/package.json b/packages/web/package.json index c5bdd78..d6e3b1b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -4,33 +4,36 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "bunx --bun vite", - "build": "tsc -b && vite build", - "lint": "biome lint . && stylelint **/*.css", - "preview": "vite preview", + "build": "react-router build", + "dev": "react-router dev", + "start": "react-router-serve ./build/server/index.js", + "typecheck": "react-router typegen && tsc", "test": "vitest" }, "dependencies": { - "effect": "=3.14.5", - "react": "=19.1.0", - "react-dom": "=19.1.0", + "@react-router/node": "7.10.1", + "@react-router/serve": "7.10.1", + "isbot": "^5.1.31", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-router": "7.10.1", "react-hook-form": "7.55.0", - "react-router-dom": "7.4.1", "vite-plugin-top-level-await": "1.5.0" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@types/lodash": "4.17.16", - "@types/react": "19.1.0", - "@types/react-dom": "19.1.1", - "@vitejs/plugin-react": "4.3.4", - "meow": "13.2.0", - "stylelint": "16.17.0", - "stylelint-config-standard": "37.0.0", - "typescript": "5.8.2", - "typescript-eslint": "8.29.0", - "vite": "6.2.4", - "vite-plugin-checker": "0.9.1", - "vitest": "3.1.1" + "@react-router/dev": "7.10.1", + "@tailwindcss/vite": "^4.1.13", + "@types/node": "^22", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "tailwindcss": "^4.1.13", + "typescript": "^5.9.2", + "vite": "^7.1.7", + "vite-tsconfig-paths": "^5.1.4", + "@biomejs/biome": "2.3.6", + "stylelint": "16.26.1", + "stylelint-config-standard": "39.0.1", + "vite-plugin-checker": "0.12.0", + "vitest": "4.0.16" } } diff --git a/packages/web/public/favicon.ico b/packages/web/public/favicon.ico index a11777c..5dbdfcd 100644 Binary files a/packages/web/public/favicon.ico and b/packages/web/public/favicon.ico differ diff --git a/packages/web/public/logo192.png b/packages/web/public/logo192.png deleted file mode 100644 index fc44b0a..0000000 Binary files a/packages/web/public/logo192.png and /dev/null differ diff --git a/packages/web/public/logo512.png b/packages/web/public/logo512.png deleted file mode 100644 index a4e47a6..0000000 Binary files a/packages/web/public/logo512.png and /dev/null differ diff --git a/packages/web/public/manifest.json b/packages/web/public/manifest.json deleted file mode 100644 index 080d6c7..0000000 --- a/packages/web/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/packages/web/public/robots.txt b/packages/web/public/robots.txt deleted file mode 100644 index e9e57dc..0000000 --- a/packages/web/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/packages/web/react-router.config.ts b/packages/web/react-router.config.ts new file mode 100644 index 0000000..6ff16f9 --- /dev/null +++ b/packages/web/react-router.config.ts @@ -0,0 +1,7 @@ +import type { Config } from "@react-router/dev/config"; + +export default { + // Config options... + // Server-side render by default, to enable SPA mode set this to `false` + ssr: true, +} satisfies Config; diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx deleted file mode 100644 index 4ed09e9..0000000 --- a/packages/web/src/App.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { - createBrowserRouter, - Navigate, - Outlet, - RouterProvider, -} from "react-router-dom"; - -const router = createBrowserRouter([ - { - path: "/", - element: , - children: [ - { - path: "/", - element: , - }, - { - path: "test", - element:

Test

, - }, - ], - }, -]); - -function App() { - return ; -} - -export default App; diff --git a/packages/web/src/index.css b/packages/web/src/index.css deleted file mode 100644 index be4b9d3..0000000 --- a/packages/web/src/index.css +++ /dev/null @@ -1,5 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx deleted file mode 100644 index bb311b2..0000000 --- a/packages/web/src/main.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import App from "./App.tsx"; -import "./index.css"; - -const root = document.getElementById("root"); - -if (root != null) { - createRoot(root).render( - - - , - ); -} else { - console.error("No #root element found"); -} diff --git a/packages/web/src/requireContext.ts b/packages/web/src/requireContext.ts deleted file mode 100644 index e471762..0000000 --- a/packages/web/src/requireContext.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext } from "react"; - -export function useRequiredContext( - c: React.Context, - error: string, -): T { - const ctx = useContext(c); - if (ctx == null) { - throw new Error(error); - } - return ctx; -} diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json index 1673d04..ca6bcbf 100644 --- a/packages/web/tsconfig.json +++ b/packages/web/tsconfig.json @@ -1,5 +1,17 @@ { "files": [], "extends": "../../tsconfig.base.json", - "include": ["src"] + "include": [ + "**/*", + "**/.server/**/*", + "**/.client/**/*", + ".react-router/types/**/*" + ], + "compilerOptions": { + "rootDirs": [".", "./.react-router/types"], + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + } } diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 707457b..2c2facc 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -1,6 +1,7 @@ +import { reactRouter } from "@react-router/dev/vite"; import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; import checker from "vite-plugin-checker"; +import tsconfigPaths from "vite-tsconfig-paths"; // https://vitejs.dev/config/ export default defineConfig({ @@ -8,9 +9,10 @@ export default defineConfig({ checker({ biome: { command: "check", dev: { command: "lint" } }, typescript: true, - stylelint: { lintCommand: "stylelint ./src/**/*.css" }, + stylelint: { lintCommand: "stylelint ./app/**/*.css" }, }), - react(), + reactRouter(), + tsconfigPaths(), ], server: { fs: { diff --git a/tsconfig.base.json b/tsconfig.base.json index 37782bc..30bda58 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -3,7 +3,7 @@ "target": "ES2022", "useDefineForClassFields": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], - "types": ["vitest/importMeta"], + "types": ["node", "vite/client", "vitest/importMeta"], "module": "ESNext", "skipLibCheck": true, @@ -16,6 +16,7 @@ "strict": true, + "esModuleInterop": true, "allowUnusedLabels": false, "allowUnreachableCode": false, "exactOptionalPropertyTypes": true, @@ -26,6 +27,8 @@ "noUncheckedIndexedAccess": true, "noUnusedLocals": false, "noUnusedParameters": false, + "verbatimModuleSyntax": true, + "resolveJsonModule": true, "checkJs": true, "verbatimModuleSyntax": true,