create-react-adam 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "lint:check": "eslint .",
11
+ "lint": "eslint . --fix",
12
+ "format:check": "prettier --check .",
13
+ "format": "prettier --write .",
14
+ "setFormatToPrecommitHook": "echo \"#!/bin/bash\\n\\nSTAGED_JS_FILES=\\$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\\\.(tsx?|js)\\$' || true)\\n\\nif [ -n \\\"\\$STAGED_JS_FILES\\\" ]; then\\n echo \\\"\\$STAGED_JS_FILES\\\" | xargs npx prettier --write --ignore-unknown || exit 1\\n echo \\\"\\$STAGED_JS_FILES\\\" | xargs npx eslint --fix || exit 1\\n echo \\\"\\$STAGED_JS_FILES\\\" | xargs git add\\nfi\" > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit",
15
+ "_itDoesNotWork": "echo '\\nšŸ” Troubleshooting the react project...\\n' && echo '\\nšŸ“¦ Step 1: Installing Node dependencies\\n' && npm install --silent && echo 'āœ… Node dependencies installed successfully!' && echo '\\nšŸ”„ Step 2: Checking if Vite dev server is running\\n' && (pgrep -f \"vite\" > /dev/null && echo 'āœ… Vite dev server is running') || echo 'āŒ Vite dev server is not running. Start it with: npm run dev' && echo '\\nāœ… Basic troubleshooting complete. If issues persist, please ask for help.'",
16
+ "itDoesNotWork": "npm run _itDoesNotWork --silent"
17
+ },
18
+ "dependencies": {
19
+ "react": "19.2.0",
20
+ "react-dom": "19.2.0",
21
+ "react-icons": "5.5.0",
22
+ "wouter": "3.7.1"
23
+ },
24
+ "devDependencies": {
25
+ "@eslint/js": "9.39.0",
26
+ "@tailwindcss/vite": "4.1.16",
27
+ "@types/react": "19.2.2",
28
+ "@types/react-dom": "19.2.2",
29
+ "@vitejs/plugin-react": "5.1.0",
30
+ "eslint": "9.39.0",
31
+ "eslint-config-prettier": "10.1.8",
32
+ "eslint-plugin-de-morgan": "2.0.0",
33
+ "eslint-plugin-import": "2.32.0",
34
+ "eslint-plugin-jsx-a11y": "6.10.2",
35
+ "eslint-plugin-promise": "7.2.1",
36
+ "eslint-plugin-react-hooks": "7.0.1",
37
+ "eslint-plugin-react-refresh": "0.4.24",
38
+ "eslint-plugin-unicorn": "62.0.0",
39
+ "prettier": "3.6.2",
40
+ "prettier-plugin-organize-imports": "4.3.0",
41
+ "prettier-plugin-tailwindcss": "0.7.1",
42
+ "tailwindcss": "4.1.16",
43
+ "typescript": "5.9.3",
44
+ "typescript-eslint": "8.46.2",
45
+ "vite": "7.1.12"
46
+ }
47
+ }
@@ -0,0 +1,25 @@
1
+ import { defineConfig, devices } from "@playwright/test";
2
+
3
+ export default defineConfig({
4
+ testDir: "./e2e",
5
+ fullyParallel: true,
6
+ forbidOnly: !!process.env.CI,
7
+ retries: process.env.CI ? 2 : 0,
8
+ workers: process.env.CI ? 1 : undefined,
9
+ reporter: [["html"], ["allure-playwright"]],
10
+ use: {
11
+ baseURL: "http://localhost:5173",
12
+ trace: "on-first-retry",
13
+ },
14
+ projects: [
15
+ {
16
+ name: "chromium",
17
+ use: { ...devices["Desktop Chrome"] },
18
+ },
19
+ ],
20
+ webServer: {
21
+ command: "npm run dev",
22
+ url: "http://localhost:5173",
23
+ reuseExistingServer: !process.env.CI,
24
+ },
25
+ });
@@ -0,0 +1,17 @@
1
+ import { Redirect, Route, Switch } from "wouter";
2
+ import About from "./pages/About";
3
+ import Home from "./pages/Home";
4
+ import NotFound from "./pages/NotFound";
5
+
6
+ const App = () => (
7
+ <Switch>
8
+ <Route path="/" component={Home} />
9
+ <Route path="/about" component={About} />
10
+ <Route path="/home">
11
+ <Redirect to="/" />
12
+ </Route>
13
+ <Route component={NotFound} />
14
+ </Switch>
15
+ );
16
+
17
+ export default App;
@@ -0,0 +1,29 @@
1
+ @import "tailwindcss";
2
+
3
+ @theme {
4
+ --font-sans: "Inter", sans-serif;
5
+
6
+ --color-brand-primary: #3b82f6;
7
+ --color-brand-primaryHover: #2563eb;
8
+ --color-brand-primaryActive: #1d4ed8;
9
+
10
+ --color-brand-secondary: #8b5cf6;
11
+ --color-brand-secondaryHover: #7c3aed;
12
+ --color-brand-secondaryActive: #6d28d9;
13
+
14
+ --color-brand-success: #22c55e;
15
+ --color-brand-warning: #f59e0b;
16
+ --color-brand-danger: #ef4444;
17
+
18
+ --color-brand-black: #0f172a;
19
+ --color-brand-white: #ffffff;
20
+
21
+ --color-brand-gray: #64748b;
22
+ --color-brand-grayLight: #e2e8f0;
23
+ --color-brand-grayDark: #334155;
24
+
25
+ --color-brand-background: #f8fafc;
26
+ --color-brand-border: #cbd5e1;
27
+
28
+ --color-brand-disabled: #94a3b8;
29
+ }
@@ -0,0 +1,13 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import App from "./App.tsx";
4
+ import "./app.css";
5
+
6
+ const rootElement = document.getElementById("root");
7
+ if (!rootElement) throw new Error("Failed to find the root element");
8
+
9
+ createRoot(rootElement).render(
10
+ <StrictMode>
11
+ <App />
12
+ </StrictMode>,
13
+ );
@@ -0,0 +1,39 @@
1
+ import { Link } from "wouter";
2
+
3
+ const About = () => {
4
+ return (
5
+ <div className="bg-brand-background flex min-h-screen flex-col items-center justify-center">
6
+ <div className="mx-auto max-w-2xl px-4 text-center">
7
+ <h1 className="text-brand-black mb-6 text-5xl font-bold">
8
+ About This Project
9
+ </h1>
10
+ <p className="text-brand-gray mb-8 text-xl">
11
+ This is a starter template created with create-react-adam. It includes
12
+ everything you need to build modern React applications.
13
+ </p>
14
+
15
+ <div className="mb-8 text-left">
16
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
17
+ Included Tools:
18
+ </h2>
19
+ <ul className="text-brand-gray space-y-2">
20
+ <li>āš›ļø React 19 with TypeScript</li>
21
+ <li>⚔ Vite for fast development</li>
22
+ <li>šŸŽØ Tailwind CSS for styling</li>
23
+ <li>šŸ›£ļø Wouter for lightweight routing</li>
24
+ <li>✨ ESLint & Prettier configured</li>
25
+ </ul>
26
+ </div>
27
+
28
+ <Link
29
+ href="/"
30
+ className="bg-brand-primary text-brand-white hover:bg-brand-primaryHover rounded-lg px-6 py-3 transition-colors"
31
+ >
32
+ Back to Home
33
+ </Link>
34
+ </div>
35
+ </div>
36
+ );
37
+ };
38
+
39
+ export default About;
@@ -0,0 +1,97 @@
1
+ import { Link } from "wouter";
2
+ import { useReactPersist } from "../../utils/Storage";
3
+ import { useUrlState } from "../../utils/useUrlState";
4
+
5
+ const About = () => {
6
+ const [sharedCount, setSharedCount] = useReactPersist("sharedCounter", 0);
7
+ const [urlCount, setUrlCount] = useUrlState("counter", 0);
8
+
9
+ return (
10
+ <div className="bg-brand-background flex min-h-screen flex-col items-center justify-center">
11
+ <div className="mx-auto max-w-2xl px-4 text-center">
12
+ <h1 className="text-brand-black mb-6 text-5xl font-bold">
13
+ About This Project
14
+ </h1>
15
+ <p className="text-brand-gray mb-8 text-xl">
16
+ This is a starter template created with create-react-adam. It includes
17
+ everything you need to build modern React applications.
18
+ </p>
19
+
20
+ <div className="mb-8 space-y-6">
21
+ <div className="bg-brand-white rounded-lg p-6 shadow-md">
22
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
23
+ Shared Counter (localStorage)
24
+ </h2>
25
+ <p className="text-brand-gray mb-4">
26
+ This counter is shared between Home and About pages
27
+ </p>
28
+ <div className="flex items-center justify-center gap-4">
29
+ <button
30
+ onClick={() => setSharedCount(sharedCount - 1)}
31
+ className="bg-brand-danger text-brand-white hover:bg-brand-danger/90 rounded-lg px-4 py-2 transition-colors"
32
+ >
33
+ -
34
+ </button>
35
+ <span className="text-brand-black text-3xl font-bold">
36
+ {sharedCount}
37
+ </span>
38
+ <button
39
+ onClick={() => setSharedCount(sharedCount + 1)}
40
+ className="bg-brand-success text-brand-white hover:bg-brand-success/90 rounded-lg px-4 py-2 transition-colors"
41
+ >
42
+ +
43
+ </button>
44
+ </div>
45
+ </div>
46
+
47
+ <div className="bg-brand-white rounded-lg p-6 shadow-md">
48
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
49
+ URL Counter
50
+ </h2>
51
+ <p className="text-brand-gray mb-4">
52
+ This counter syncs with URL parameters
53
+ </p>
54
+ <div className="flex items-center justify-center gap-4">
55
+ <button
56
+ onClick={() => setUrlCount(urlCount - 1)}
57
+ className="bg-brand-danger text-brand-white hover:bg-brand-danger/90 rounded-lg px-4 py-2 transition-colors"
58
+ >
59
+ -
60
+ </button>
61
+ <span className="text-brand-black text-3xl font-bold">
62
+ {urlCount}
63
+ </span>
64
+ <button
65
+ onClick={() => setUrlCount(urlCount + 1)}
66
+ className="bg-brand-success text-brand-white hover:bg-brand-success/90 rounded-lg px-4 py-2 transition-colors"
67
+ >
68
+ +
69
+ </button>
70
+ </div>
71
+ </div>
72
+ </div>
73
+
74
+ <div className="mb-8 text-left">
75
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
76
+ Included Tools:
77
+ </h2>
78
+ <ul className="text-brand-gray space-y-2">
79
+ <li>āš›ļø React 19 with TypeScript</li>
80
+ <li>⚔ Vite for fast development</li>
81
+ <li>šŸŽØ Tailwind CSS for styling</li>
82
+ <li>šŸ›£ļø Wouter for lightweight routing</li>
83
+ <li>✨ ESLint & Prettier configured</li>
84
+ </ul>
85
+ </div>
86
+ <Link
87
+ href="/"
88
+ className="bg-brand-primary text-brand-white hover:bg-brand-primaryHover rounded-lg px-6 py-3 transition-colors"
89
+ >
90
+ Back to Home
91
+ </Link>
92
+ </div>
93
+ </div>
94
+ );
95
+ };
96
+
97
+ export default About;
@@ -0,0 +1,53 @@
1
+ import { Link } from "wouter";
2
+
3
+ const Home = () => {
4
+ return (
5
+ <div className="bg-brand-background flex min-h-screen flex-col items-center justify-center">
6
+ <div className="mx-auto max-w-2xl px-4 text-center">
7
+ <h1 className="text-brand-black mb-6 text-5xl font-bold">
8
+ Welcome to Your React App
9
+ </h1>
10
+ <p className="text-brand-gray mb-8 text-xl">
11
+ Built with React, TypeScript, Vite, Wouter, and Tailwind CSS
12
+ </p>
13
+
14
+ <div className="mb-8">
15
+ <div className="bg-brand-white rounded-lg p-6 shadow-md">
16
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
17
+ Get Started
18
+ </h2>
19
+ <p className="text-brand-gray mb-4">
20
+ Edit{" "}
21
+ <code className="bg-brand-grayLight rounded px-2 py-1">
22
+ src/pages/Home/index.tsx
23
+ </code>{" "}
24
+ to customize this page.
25
+ </p>
26
+ <p className="text-brand-gray">
27
+ This is a clean starting point for your React application.
28
+ </p>
29
+ </div>
30
+ </div>
31
+
32
+ <div className="flex justify-center gap-4">
33
+ <Link
34
+ href="/about"
35
+ className="bg-brand-primary text-brand-white hover:bg-brand-primaryHover rounded-lg px-6 py-3 transition-colors"
36
+ >
37
+ About Page
38
+ </Link>
39
+ <a
40
+ href="https://react.dev"
41
+ target="_blank"
42
+ rel="noopener noreferrer"
43
+ className="border-brand-border bg-brand-white text-brand-gray hover:bg-brand-grayLight rounded-lg border px-6 py-3 transition-colors"
44
+ >
45
+ Learn React
46
+ </a>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ export default Home;
@@ -0,0 +1,94 @@
1
+ import { Link } from "wouter";
2
+ import { useReactPersist } from "../../utils/Storage";
3
+ import { useUrlState } from "../../utils/useUrlState";
4
+
5
+ const Home = () => {
6
+ const [sharedCount, setSharedCount] = useReactPersist("sharedCounter", 0);
7
+ const [urlCount, setUrlCount] = useUrlState("counter", 0);
8
+
9
+ return (
10
+ <div className="bg-brand-background flex min-h-screen flex-col items-center justify-center">
11
+ <div className="mx-auto max-w-2xl px-4 text-center">
12
+ <h1 className="text-brand-black mb-6 text-5xl font-bold">
13
+ Welcome to Your React App
14
+ </h1>
15
+ <p className="text-brand-gray mb-8 text-xl">
16
+ Built with React, TypeScript, Vite, Wouter, and Tailwind CSS
17
+ </p>
18
+
19
+ <div className="mb-8 space-y-6">
20
+ <div className="bg-brand-white rounded-lg p-6 shadow-md">
21
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
22
+ Shared Counter (localStorage)
23
+ </h2>
24
+ <p className="text-brand-gray mb-4">
25
+ This counter is shared between Home and About pages
26
+ </p>
27
+ <div className="flex items-center justify-center gap-4">
28
+ <button
29
+ onClick={() => setSharedCount(sharedCount - 1)}
30
+ className="bg-brand-danger text-brand-white hover:bg-brand-danger/90 rounded-lg px-4 py-2 transition-colors"
31
+ >
32
+ -
33
+ </button>
34
+ <span className="text-brand-black text-3xl font-bold">
35
+ {sharedCount}
36
+ </span>
37
+ <button
38
+ onClick={() => setSharedCount(sharedCount + 1)}
39
+ className="bg-brand-success text-brand-white hover:bg-brand-success/90 rounded-lg px-4 py-2 transition-colors"
40
+ >
41
+ +
42
+ </button>
43
+ </div>
44
+ </div>
45
+
46
+ <div className="bg-brand-white rounded-lg p-6 shadow-md">
47
+ <h2 className="text-brand-black mb-4 text-2xl font-semibold">
48
+ URL Counter
49
+ </h2>
50
+ <p className="text-brand-gray mb-4">
51
+ This counter syncs with URL parameters
52
+ </p>
53
+ <div className="flex items-center justify-center gap-4">
54
+ <button
55
+ onClick={() => setUrlCount(urlCount - 1)}
56
+ className="bg-brand-danger text-brand-white hover:bg-brand-danger/90 rounded-lg px-4 py-2 transition-colors"
57
+ >
58
+ -
59
+ </button>
60
+ <span className="text-brand-black text-3xl font-bold">
61
+ {urlCount}
62
+ </span>
63
+ <button
64
+ onClick={() => setUrlCount(urlCount + 1)}
65
+ className="bg-brand-success text-brand-white hover:bg-brand-success/90 rounded-lg px-4 py-2 transition-colors"
66
+ >
67
+ +
68
+ </button>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div className="flex justify-center gap-4">
74
+ <Link
75
+ href="/about"
76
+ className="bg-brand-primary text-brand-white hover:bg-brand-primaryHover rounded-lg px-6 py-3 transition-colors"
77
+ >
78
+ About Page
79
+ </Link>
80
+ <a
81
+ href="https://react.dev"
82
+ target="_blank"
83
+ rel="noopener noreferrer"
84
+ className="border-brand-border bg-brand-white text-brand-gray hover:bg-brand-grayLight rounded-lg border px-6 py-3 transition-colors"
85
+ >
86
+ Learn React
87
+ </a>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ );
92
+ };
93
+
94
+ export default Home;
@@ -0,0 +1,23 @@
1
+ import { Link } from "wouter";
2
+
3
+ const NotFound = () => (
4
+ <div className="bg-brand-background flex min-h-screen flex-col items-center justify-center">
5
+ <div className="text-center">
6
+ <h1 className="text-brand-black text-9xl font-bold">404</h1>
7
+ <p className="text-brand-black mt-4 text-2xl font-semibold">
8
+ Page Not Found
9
+ </p>
10
+ <p className="text-brand-gray mt-2">
11
+ The page you're looking for doesn't exist.
12
+ </p>
13
+ <Link
14
+ href="/"
15
+ className="bg-brand-primary text-brand-white hover:bg-brand-primaryHover mt-6 inline-block rounded-lg px-6 py-3 transition-colors"
16
+ >
17
+ Go Home
18
+ </Link>
19
+ </div>
20
+ </div>
21
+ );
22
+
23
+ export default NotFound;
@@ -0,0 +1,22 @@
1
+ import { DependencyList, useEffect } from "react";
2
+
3
+ export function useInternetConnected(
4
+ callback: () => void,
5
+ dependencies: DependencyList = [],
6
+ ) {
7
+ useEffect(() => {
8
+ if (navigator.onLine) {
9
+ callback();
10
+ }
11
+ const handleOnline = () => {
12
+ callback();
13
+ };
14
+
15
+ globalThis.addEventListener("online", handleOnline);
16
+
17
+ return () => {
18
+ globalThis.removeEventListener("online", handleOnline);
19
+ };
20
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21
+ }, dependencies);
22
+ }
@@ -0,0 +1,86 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+
3
+ interface StorageOptions {
4
+ secondsTillExpiry?: number;
5
+ }
6
+
7
+ interface StorageValue<T> {
8
+ value: T;
9
+ expiry?: number;
10
+ }
11
+
12
+ class Storage {
13
+ load<T>(key: string, defaultValue: T): T {
14
+ try {
15
+ const item = localStorage.getItem(key);
16
+ if (!item) {
17
+ return defaultValue;
18
+ }
19
+
20
+ const parsed = JSON.parse(item) as StorageValue<T>;
21
+
22
+ if (parsed.expiry && Date.now() > parsed.expiry) {
23
+ localStorage.removeItem(key);
24
+ return defaultValue;
25
+ }
26
+
27
+ return parsed.value;
28
+ } catch {
29
+ return defaultValue;
30
+ }
31
+ }
32
+
33
+ loadAsString(key: string): string | null {
34
+ return localStorage.getItem(key);
35
+ }
36
+
37
+ save<T>(key: string, value: T, options?: StorageOptions): void {
38
+ try {
39
+ const storageValue: StorageValue<T> = {
40
+ value,
41
+ ...(options?.secondsTillExpiry && {
42
+ expiry: Date.now() + options.secondsTillExpiry * 1000,
43
+ }),
44
+ };
45
+ localStorage.setItem(key, JSON.stringify(storageValue));
46
+ } catch (error) {
47
+ console.error("Failed to save to localStorage:", error);
48
+ }
49
+ }
50
+
51
+ remove(key: string): void {
52
+ localStorage.removeItem(key);
53
+ }
54
+
55
+ clear(): void {
56
+ localStorage.clear();
57
+ }
58
+ }
59
+
60
+ const storage = new Storage();
61
+
62
+ export function useReactPersist<T>(
63
+ key: string,
64
+ defaultValue: T,
65
+ options?: StorageOptions,
66
+ ): [T, (value: T | ((prev: T) => T)) => void] {
67
+ const [state, setState] = useState<T>(() => storage.load(key, defaultValue));
68
+
69
+ useEffect(() => {
70
+ storage.save(key, state, options);
71
+ }, [key, state, options]);
72
+
73
+ const setPersistedState = useCallback((value: T | ((prev: T) => T)) => {
74
+ setState((prevState) => {
75
+ const newState =
76
+ typeof value === "function"
77
+ ? (value as (prev: T) => T)(prevState)
78
+ : value;
79
+ return newState;
80
+ });
81
+ }, []);
82
+
83
+ return [state, setPersistedState];
84
+ }
85
+
86
+ export default storage;
@@ -0,0 +1,3 @@
1
+ export default function classNames(...classes: string[]) {
2
+ return classes.filter(Boolean).join(" ");
3
+ }
@@ -0,0 +1,27 @@
1
+ import { useCallback } from "react";
2
+ import { useLocation, useSearch } from "wouter";
3
+
4
+ export function useUrlState(
5
+ key: string,
6
+ defaultValue: number,
7
+ ): [number, (value: number) => void] {
8
+ const [, setLocation] = useLocation();
9
+ const searchParams = useSearch();
10
+
11
+ const params = new URLSearchParams(searchParams);
12
+ const urlValue = params.get(key);
13
+ const value = urlValue ? Number.parseInt(urlValue, 10) : defaultValue;
14
+
15
+ const setValue = useCallback(
16
+ (newValue: number) => {
17
+ const currentParams = new URLSearchParams(globalThis.location.search);
18
+ currentParams.set(key, newValue.toString());
19
+ setLocation(
20
+ `${globalThis.location.pathname}?${currentParams.toString()}`,
21
+ );
22
+ },
23
+ [key, setLocation],
24
+ );
25
+
26
+ return [value, setValue];
27
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "isolatedModules": true,
11
+ "moduleDetection": "force",
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedSideEffectImports": true
19
+ },
20
+ "include": ["src"]
21
+ }
@@ -0,0 +1,9 @@
1
+ // eslint-disable-next-line import/no-unresolved
2
+ import tailwindcss from "@tailwindcss/vite";
3
+ // eslint-disable-next-line import/no-unresolved
4
+ import react from "@vitejs/plugin-react";
5
+ import { defineConfig } from "vite";
6
+
7
+ export default defineConfig({
8
+ plugins: [react(), tailwindcss()],
9
+ });