create-lumen 2.0.0-beta.1 → 2.0.0-beta.3

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.
Files changed (87) hide show
  1. package/README.md +23 -22
  2. package/bin/cli.js +1 -6
  3. package/package.json +2 -2
  4. package/schema/fixtures/v2/valid/next-app-router-feature-based.json +1 -1
  5. package/schema/fixtures/v2/valid/next-app-router-none.json +1 -1
  6. package/schema/fixtures/v2/valid/next-pages-router-feature-based.json +1 -1
  7. package/schema/fixtures/v2/valid/next-pages-router-none.json +1 -1
  8. package/schema/lumen.config.v2.json +30 -2
  9. package/src/agent-context.js +71 -47
  10. package/src/cleanup.js +34 -1
  11. package/src/cli-args.js +79 -2
  12. package/src/configure.js +36 -8
  13. package/src/css.js +3 -4
  14. package/src/dependencies.js +42 -15
  15. package/src/engine/capabilities.js +51 -2
  16. package/src/engine/composition.js +282 -23
  17. package/src/env.js +159 -4
  18. package/src/format.js +14 -6
  19. package/src/injector.js +66 -14
  20. package/src/main.js +62 -17
  21. package/src/manifest/emit.js +35 -6
  22. package/src/manifest/paths.js +1 -25
  23. package/src/manifest/schema.js +32 -2
  24. package/src/prompts.js +101 -11
  25. package/src/readme.js +0 -16
  26. package/src/utils/dev-server.js +6 -1
  27. package/templates/.env.example +1 -1
  28. package/templates/architectures/feature-based/scripts/create-feature.mjs +2 -2
  29. package/templates/conditional/database/prisma/db.js +14 -0
  30. package/templates/conditional/database/prisma/db.ts +16 -0
  31. package/templates/conditional/database/prisma/schema.prisma +9 -0
  32. package/templates/conditional/database/prisma/seed.js +16 -0
  33. package/templates/conditional/database/prisma/seed.ts +16 -0
  34. package/templates/conditional/docker/.dockerignore +10 -0
  35. package/templates/conditional/docker/Dockerfile +57 -0
  36. package/templates/conditional/docker/docker-compose.yml +35 -0
  37. package/templates/conditional/linting/biome/biome.json +17 -11
  38. package/templates/conditional/linting/eslint/next/eslint.config.mjs +2 -11
  39. package/templates/conditional/next/route-groups/js/src/app/(admin)/layout.jsx +23 -0
  40. package/templates/conditional/next/route-groups/js/src/app/(auth)/layout.jsx +17 -0
  41. package/templates/conditional/next/route-groups/js/src/app/(private)/layout.jsx +27 -0
  42. package/templates/conditional/next/route-groups/js/src/app/(public)/layout.jsx +28 -0
  43. package/templates/conditional/next/route-groups/js/src/app/(public)/page.jsx +38 -0
  44. package/templates/conditional/next/route-groups/js/src/middleware.js +48 -0
  45. package/templates/conditional/next/route-groups/ts/src/app/(admin)/layout.tsx +28 -0
  46. package/templates/conditional/next/route-groups/ts/src/app/(auth)/layout.tsx +22 -0
  47. package/templates/conditional/next/route-groups/ts/src/app/(private)/layout.tsx +32 -0
  48. package/templates/conditional/next/route-groups/ts/src/app/(public)/layout.tsx +33 -0
  49. package/templates/conditional/next/route-groups/ts/src/app/(public)/page.tsx +38 -0
  50. package/templates/conditional/next/route-groups/ts/src/middleware.ts +49 -0
  51. package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.jsx +10 -0
  52. package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.tsx +10 -0
  53. package/templates/conditional/testing/jest/next/jest.config.mjs +20 -0
  54. package/templates/conditional/testing/jest/next/jest.config.ts +20 -0
  55. package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.jsx +10 -0
  56. package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.tsx +10 -0
  57. package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.jsx +11 -0
  58. package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.tsx +11 -0
  59. package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.jsx +11 -0
  60. package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.tsx +11 -0
  61. package/templates/conditional/testing/vitest/next/vitest.config.js +16 -0
  62. package/templates/conditional/testing/vitest/next/vitest.config.ts +16 -0
  63. package/templates/css/tailwind/postcss.config.mjs +3 -1
  64. package/schema/fixtures/v2/valid/next-app-router-hybrid.json +0 -35
  65. package/schema/fixtures/v2/valid/next-pages-router-hybrid.json +0 -35
  66. package/templates/architectures/hybrid/main.jsx +0 -10
  67. package/templates/architectures/hybrid/main.tsx +0 -10
  68. package/templates/architectures/hybrid/src/App.jsx +0 -20
  69. package/templates/architectures/hybrid/src/App.tsx +0 -20
  70. package/templates/architectures/hybrid/src/features/.gitkeep +0 -1
  71. package/templates/architectures/hybrid/src/shared/components/ui/Button.jsx +0 -31
  72. package/templates/architectures/hybrid/src/shared/components/ui/Button.tsx +0 -36
  73. package/templates/architectures/hybrid/src/shared/components/ui/index.js +0 -1
  74. package/templates/architectures/hybrid/src/shared/components/ui/index.ts +0 -1
  75. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.jsx +0 -28
  76. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.ts +0 -28
  77. package/templates/architectures/hybrid/src/shared/layouts/.gitkeep +0 -0
  78. package/templates/architectures/hybrid/src/shared/stores/.gitkeep +0 -0
  79. package/templates/architectures/hybrid/src/shared/styles/.gitkeep +0 -0
  80. package/templates/architectures/hybrid/src/shared/types/index.js +0 -20
  81. package/templates/architectures/hybrid/src/shared/types/index.ts +0 -16
  82. package/templates/architectures/hybrid/src/shared/utils/cn.jsx +0 -3
  83. package/templates/architectures/hybrid/src/shared/utils/cn.ts +0 -5
  84. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.jsx +0 -11
  85. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.tsx +0 -11
  86. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.jsx +0 -10
  87. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.tsx +0 -10
@@ -0,0 +1,38 @@
1
+ import Link from "next/link";
2
+
3
+ export default function PublicPage() {
4
+ return (
5
+ <div className="max-w-4xl mx-auto py-12 px-6">
6
+ <h1 className="text-4xl font-extrabold tracking-tight mb-4">
7
+ Welcome to Route Groups Preset
8
+ </h1>
9
+ <p className="text-lg text-gray-600 mb-8">
10
+ This application is organized using Next.js App Router route groups:
11
+ (public), (auth), (private), and (admin).
12
+ </p>
13
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
14
+ <div className="border rounded-lg p-6 shadow-sm">
15
+ <h2 className="text-xl font-semibold mb-2">Auth Group</h2>
16
+ <p className="text-gray-500 mb-4">Authentication screens and login layouts.</p>
17
+ <Link href="/login" className="text-blue-600 hover:underline">
18
+ Go to Login &rarr;
19
+ </Link>
20
+ </div>
21
+ <div className="border rounded-lg p-6 shadow-sm">
22
+ <h2 className="text-xl font-semibold mb-2">Private Group</h2>
23
+ <p className="text-gray-500 mb-4">Protected routes guarded by auth proxy middleware.</p>
24
+ <Link href="/dashboard" className="text-blue-600 hover:underline">
25
+ Go to Dashboard &rarr;
26
+ </Link>
27
+ </div>
28
+ <div className="border rounded-lg p-6 shadow-sm">
29
+ <h2 className="text-xl font-semibold mb-2">Admin Group</h2>
30
+ <p className="text-gray-500 mb-4">Elevated management section requiring admin role.</p>
31
+ <Link href="/admin" className="text-blue-600 hover:underline">
32
+ Go to Admin &rarr;
33
+ </Link>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,48 @@
1
+ import { NextResponse } from "next/server";
2
+
3
+ /**
4
+ * Auth Proxy / Route Guard Middleware
5
+ *
6
+ * Enforces role-based access control and session verification:
7
+ * - Unauthenticated requests to private or admin routes redirect to /login
8
+ * - Authenticated requests lacking the "admin" role redirect away from /admin
9
+ */
10
+ export function middleware(request) {
11
+ const { pathname } = request.nextUrl;
12
+
13
+ const authToken = request.cookies.get("auth-token")?.value;
14
+ const userRole = request.cookies.get("user-role")?.value || "user";
15
+
16
+ const isAuthRoute = pathname.startsWith("/login") || pathname.startsWith("/register");
17
+ const isPrivateRoute = pathname.startsWith("/dashboard") || pathname.startsWith("/settings");
18
+ const isAdminRoute = pathname.startsWith("/admin");
19
+
20
+ // If unauthenticated user tries to access protected areas, redirect to /login
21
+ if ((isPrivateRoute || isAdminRoute) && !authToken) {
22
+ const loginUrl = new URL("/login", request.url);
23
+ loginUrl.searchParams.set("from", pathname);
24
+ return NextResponse.redirect(loginUrl);
25
+ }
26
+
27
+ // If authenticated user without admin role accesses admin area, forbid or redirect to dashboard
28
+ if (isAdminRoute && userRole !== "admin") {
29
+ return NextResponse.redirect(new URL("/dashboard", request.url));
30
+ }
31
+
32
+ // If already authenticated user visits auth routes, redirect to dashboard
33
+ if (isAuthRoute && authToken) {
34
+ return NextResponse.redirect(new URL("/dashboard", request.url));
35
+ }
36
+
37
+ return NextResponse.next();
38
+ }
39
+
40
+ export const config = {
41
+ matcher: [
42
+ "/dashboard/:path*",
43
+ "/settings/:path*",
44
+ "/admin/:path*",
45
+ "/login",
46
+ "/register",
47
+ ],
48
+ };
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import Link from "next/link";
3
+
4
+ export default function AdminLayout({
5
+ children,
6
+ }: Readonly<{
7
+ children: ReactNode;
8
+ }>) {
9
+ return (
10
+ <div className="min-h-screen flex flex-col">
11
+ <header className="bg-red-700 text-white px-6 py-3 flex items-center justify-between text-sm">
12
+ <div className="font-semibold flex items-center gap-2">
13
+ <span className="inline-block w-2 h-2 rounded-full bg-white animate-pulse" />
14
+ Admin Panel (Restricted Access)
15
+ </div>
16
+ <nav className="flex gap-4">
17
+ <Link href="/admin" className="underline hover:no-underline">
18
+ Management
19
+ </Link>
20
+ <Link href="/dashboard" className="underline hover:no-underline">
21
+ Exit to Dashboard
22
+ </Link>
23
+ </nav>
24
+ </header>
25
+ <main className="flex-1 p-8 bg-gray-100 dark:bg-gray-900">{children}</main>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ import Link from "next/link";
3
+
4
+ export default function AuthLayout({
5
+ children,
6
+ }: Readonly<{
7
+ children: ReactNode;
8
+ }>) {
9
+ return (
10
+ <div className="min-h-screen flex flex-col justify-center items-center bg-gray-50 dark:bg-gray-950 p-4">
11
+ <div className="w-full max-w-md bg-white dark:bg-gray-900 shadow-md rounded-lg p-6 border border-gray-200 dark:border-gray-800">
12
+ <div className="mb-6 text-center">
13
+ <Link href="/" className="text-xl font-bold">
14
+ Lumen Authentication
15
+ </Link>
16
+ <p className="text-sm text-gray-500 mt-1">Sign in to your account</p>
17
+ </div>
18
+ {children}
19
+ </div>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,32 @@
1
+ import type { ReactNode } from "react";
2
+ import Link from "next/link";
3
+
4
+ export default function PrivateLayout({
5
+ children,
6
+ }: Readonly<{
7
+ children: ReactNode;
8
+ }>) {
9
+ return (
10
+ <div className="min-h-screen flex">
11
+ <aside className="w-64 border-r bg-gray-50 dark:bg-gray-950 p-6 flex flex-col justify-between">
12
+ <div>
13
+ <div className="font-bold text-lg mb-6">
14
+ <Link href="/dashboard">Private Dashboard</Link>
15
+ </div>
16
+ <nav className="flex flex-col gap-3 text-sm">
17
+ <Link href="/dashboard" className="text-gray-700 dark:text-gray-200 hover:text-black">
18
+ Overview
19
+ </Link>
20
+ <Link href="/settings" className="text-gray-700 dark:text-gray-200 hover:text-black">
21
+ Settings
22
+ </Link>
23
+ </nav>
24
+ </div>
25
+ <div className="text-xs text-gray-400">
26
+ Protected Area &bull; Authenticated
27
+ </div>
28
+ </aside>
29
+ <main className="flex-1 p-8">{children}</main>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from "react";
2
+ import Link from "next/link";
3
+
4
+ export default function PublicLayout({
5
+ children,
6
+ }: Readonly<{
7
+ children: ReactNode;
8
+ }>) {
9
+ return (
10
+ <div className="min-h-screen flex flex-col">
11
+ <header className="border-b px-6 py-4 flex items-center justify-between">
12
+ <div className="font-bold text-lg">
13
+ <Link href="/">Lumen App</Link>
14
+ </div>
15
+ <nav className="flex gap-4 text-sm">
16
+ <Link href="/login" className="hover:underline">
17
+ Sign In
18
+ </Link>
19
+ <Link href="/dashboard" className="hover:underline">
20
+ Dashboard
21
+ </Link>
22
+ <Link href="/admin" className="hover:underline">
23
+ Admin
24
+ </Link>
25
+ </nav>
26
+ </header>
27
+ <main className="flex-1">{children}</main>
28
+ <footer className="border-t px-6 py-4 text-center text-sm text-gray-500">
29
+ Public Area &copy; {new Date().getFullYear()}
30
+ </footer>
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,38 @@
1
+ import Link from "next/link";
2
+
3
+ export default function PublicPage() {
4
+ return (
5
+ <div className="max-w-4xl mx-auto py-12 px-6">
6
+ <h1 className="text-4xl font-extrabold tracking-tight mb-4">
7
+ Welcome to Route Groups Preset
8
+ </h1>
9
+ <p className="text-lg text-gray-600 mb-8">
10
+ This application is organized using Next.js App Router route groups:
11
+ (public), (auth), (private), and (admin).
12
+ </p>
13
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
14
+ <div className="border rounded-lg p-6 shadow-sm">
15
+ <h2 className="text-xl font-semibold mb-2">Auth Group</h2>
16
+ <p className="text-gray-500 mb-4">Authentication screens and login layouts.</p>
17
+ <Link href="/login" className="text-blue-600 hover:underline">
18
+ Go to Login &rarr;
19
+ </Link>
20
+ </div>
21
+ <div className="border rounded-lg p-6 shadow-sm">
22
+ <h2 className="text-xl font-semibold mb-2">Private Group</h2>
23
+ <p className="text-gray-500 mb-4">Protected routes guarded by auth proxy middleware.</p>
24
+ <Link href="/dashboard" className="text-blue-600 hover:underline">
25
+ Go to Dashboard &rarr;
26
+ </Link>
27
+ </div>
28
+ <div className="border rounded-lg p-6 shadow-sm">
29
+ <h2 className="text-xl font-semibold mb-2">Admin Group</h2>
30
+ <p className="text-gray-500 mb-4">Elevated management section requiring admin role.</p>
31
+ <Link href="/admin" className="text-blue-600 hover:underline">
32
+ Go to Admin &rarr;
33
+ </Link>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,49 @@
1
+ import { NextResponse } from "next/server";
2
+ import type { NextRequest } from "next/server";
3
+
4
+ /**
5
+ * Auth Proxy / Route Guard Middleware
6
+ *
7
+ * Enforces role-based access control and session verification:
8
+ * - Unauthenticated requests to private or admin routes redirect to /login
9
+ * - Authenticated requests lacking the "admin" role redirect away from /admin
10
+ */
11
+ export function middleware(request: NextRequest) {
12
+ const { pathname } = request.nextUrl;
13
+
14
+ const authToken = request.cookies.get("auth-token")?.value;
15
+ const userRole = request.cookies.get("user-role")?.value || "user";
16
+
17
+ const isAuthRoute = pathname.startsWith("/login") || pathname.startsWith("/register");
18
+ const isPrivateRoute = pathname.startsWith("/dashboard") || pathname.startsWith("/settings");
19
+ const isAdminRoute = pathname.startsWith("/admin");
20
+
21
+ // If unauthenticated user tries to access protected areas, redirect to /login
22
+ if ((isPrivateRoute || isAdminRoute) && !authToken) {
23
+ const loginUrl = new URL("/login", request.url);
24
+ loginUrl.searchParams.set("from", pathname);
25
+ return NextResponse.redirect(loginUrl);
26
+ }
27
+
28
+ // If authenticated user without admin role accesses admin area, forbid or redirect to dashboard
29
+ if (isAdminRoute && userRole !== "admin") {
30
+ return NextResponse.redirect(new URL("/dashboard", request.url));
31
+ }
32
+
33
+ // If already authenticated user visits auth routes, redirect to dashboard
34
+ if (isAuthRoute && authToken) {
35
+ return NextResponse.redirect(new URL("/dashboard", request.url));
36
+ }
37
+
38
+ return NextResponse.next();
39
+ }
40
+
41
+ export const config = {
42
+ matcher: [
43
+ "/dashboard/:path*",
44
+ "/settings/:path*",
45
+ "/admin/:path*",
46
+ "/login",
47
+ "/register",
48
+ ],
49
+ };
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Home from "@/app/page";
4
+
5
+ describe("Home", () => {
6
+ it("renders a heading", () => {
7
+ render(<Home />);
8
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Home from "@/app/page";
4
+
5
+ describe("Home", () => {
6
+ it("renders a heading", () => {
7
+ render(<Home />);
8
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,20 @@
1
+ import nextJest from "next/jest.js";
2
+
3
+ const createJestConfig = nextJest({
4
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
5
+ dir: "./",
6
+ });
7
+
8
+ // Add any custom config to be passed to Jest
9
+ /** @type {import('jest').Config} */
10
+ const config = {
11
+ coverageProvider: "v8",
12
+ testEnvironment: "jsdom",
13
+ setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
14
+ moduleNameMapper: {
15
+ "^@/(.*)$": "<rootDir>/src/$1",
16
+ },
17
+ };
18
+
19
+ // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
20
+ export default createJestConfig(config);
@@ -0,0 +1,20 @@
1
+ import type { Config } from "jest";
2
+ import nextJest from "next/jest.js";
3
+
4
+ const createJestConfig = nextJest({
5
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
6
+ dir: "./",
7
+ });
8
+
9
+ // Add any custom config to be passed to Jest
10
+ const config: Config = {
11
+ coverageProvider: "v8",
12
+ testEnvironment: "jsdom",
13
+ setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
14
+ moduleNameMapper: {
15
+ "^@/(.*)$": "<rootDir>/src/$1",
16
+ },
17
+ };
18
+
19
+ // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
20
+ export default createJestConfig(config);
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Home from "@/pages/index";
4
+
5
+ describe("Home", () => {
6
+ it("renders a heading", () => {
7
+ render(<Home />);
8
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Home from "@/pages/index";
4
+
5
+ describe("Home", () => {
6
+ it("renders a heading", () => {
7
+ render(<Home />);
8
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,11 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { describe, it, expect } from "vitest";
4
+ import Home from "@/app/page";
5
+
6
+ describe("Home", () => {
7
+ it("renders a heading", () => {
8
+ render(<Home />);
9
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { describe, it, expect } from "vitest";
4
+ import Home from "@/app/page";
5
+
6
+ describe("Home", () => {
7
+ it("renders a heading", () => {
8
+ render(<Home />);
9
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { describe, it, expect } from "vitest";
4
+ import Home from "@/pages/index";
5
+
6
+ describe("Home", () => {
7
+ it("renders a heading", () => {
8
+ render(<Home />);
9
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { describe, it, expect } from "vitest";
4
+ import Home from "@/pages/index";
5
+
6
+ describe("Home", () => {
7
+ it("renders a heading", () => {
8
+ render(<Home />);
9
+ expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,16 @@
1
+ import path from "node:path";
2
+ import { defineConfig } from "vitest/config";
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ "@": path.resolve(__dirname, "./src"),
8
+ },
9
+ },
10
+ test: {
11
+ globals: true,
12
+ environment: "jsdom",
13
+ setupFiles: "./src/test/setup.js",
14
+ css: true,
15
+ },
16
+ });
@@ -0,0 +1,16 @@
1
+ import path from "node:path";
2
+ import { defineConfig } from "vitest/config";
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ "@": path.resolve(__dirname, "./src"),
8
+ },
9
+ },
10
+ test: {
11
+ globals: true,
12
+ environment: "jsdom",
13
+ setupFiles: "./src/test/setup.ts",
14
+ css: true,
15
+ },
16
+ });
@@ -1,5 +1,7 @@
1
- export default {
1
+ const config = {
2
2
  plugins: {
3
3
  "@tailwindcss/postcss": {},
4
4
  },
5
5
  };
6
+
7
+ export default config;
@@ -1,35 +0,0 @@
1
- {
2
- "$schema": "https://lumen-sh.github.io/create-lumen/schema/lumen.config.v2.json",
3
- "manifestVersion": 2,
4
- "framework": {
5
- "name": "next",
6
- "variant": "app-router",
7
- "bundler": "turbopack",
8
- "adapter": "node"
9
- },
10
- "styling": {
11
- "engine": "tailwind"
12
- },
13
- "architecture": {
14
- "type": "hybrid"
15
- },
16
- "ui": {
17
- "kit": "none"
18
- },
19
- "docs": {
20
- "language": "en"
21
- },
22
- "paths": {
23
- "features": "src/features",
24
- "components": "src/shared/components",
25
- "services": "src/shared/services",
26
- "hooks": "src/shared/hooks",
27
- "pages": "app",
28
- "ui": "src/shared/components/ui"
29
- },
30
- "tooling": {
31
- "language": "ts",
32
- "linter": "eslint",
33
- "formatter": "prettier"
34
- }
35
- }
@@ -1,35 +0,0 @@
1
- {
2
- "$schema": "https://lumen-sh.github.io/create-lumen/schema/lumen.config.v2.json",
3
- "manifestVersion": 2,
4
- "framework": {
5
- "name": "next",
6
- "variant": "pages-router",
7
- "bundler": "turbopack",
8
- "adapter": "static"
9
- },
10
- "styling": {
11
- "engine": "tailwind"
12
- },
13
- "architecture": {
14
- "type": "hybrid"
15
- },
16
- "ui": {
17
- "kit": "none"
18
- },
19
- "docs": {
20
- "language": "es"
21
- },
22
- "paths": {
23
- "features": "src/features",
24
- "components": "src/shared/components",
25
- "services": "src/shared/services",
26
- "hooks": "src/shared/hooks",
27
- "pages": "pages",
28
- "ui": "src/shared/components/ui"
29
- },
30
- "tooling": {
31
- "language": "js",
32
- "linter": "eslint",
33
- "formatter": "prettier"
34
- }
35
- }
@@ -1,10 +0,0 @@
1
- import React from "react";
2
- import ReactDOM from "react-dom/client";
3
- import App from "./App";
4
- import "./shared/styles/globals.css";
5
-
6
- ReactDOM.createRoot(document.getElementById("root")).render(
7
- <React.StrictMode>
8
- <App />
9
- </React.StrictMode>
10
- );
@@ -1,10 +0,0 @@
1
- import React from "react";
2
- import ReactDOM from "react-dom/client";
3
- import App from "./App";
4
- import "./shared/styles/globals.css";
5
-
6
- ReactDOM.createRoot(document.getElementById("root")!).render(
7
- <React.StrictMode>
8
- <App />
9
- </React.StrictMode>
10
- );
@@ -1,20 +0,0 @@
1
- import { Button } from "@/shared/components/ui/Button";
2
-
3
- export default function App() {
4
- return (
5
- <div
6
- style={{
7
- minHeight: "100vh",
8
- display: "flex",
9
- flexDirection: "column",
10
- alignItems: "center",
11
- justifyContent: "center",
12
- backgroundColor: "var(--color-bg, #ffffff)",
13
- }}
14
- >
15
- <h1>Welcome to Lumen</h1>
16
- <p>Hybrid architecture (src/features + src/shared).</p>
17
- <Button>Click me</Button>
18
- </div>
19
- );
20
- }
@@ -1,20 +0,0 @@
1
- import { Button } from "@/shared/components/ui/Button";
2
-
3
- export default function App() {
4
- return (
5
- <div
6
- style={{
7
- minHeight: "100vh",
8
- display: "flex",
9
- flexDirection: "column",
10
- alignItems: "center",
11
- justifyContent: "center",
12
- backgroundColor: "var(--color-bg, #ffffff)",
13
- }}
14
- >
15
- <h1>Welcome to Lumen</h1>
16
- <p>Hybrid architecture (src/features + src/shared).</p>
17
- <Button>Click me</Button>
18
- </div>
19
- );
20
- }
@@ -1,31 +0,0 @@
1
- export function Button({
2
- variant = "primary",
3
- children,
4
- className,
5
- ...props
6
- }) {
7
- const styles = {
8
- primary: { backgroundColor: "var(--color-primary)", color: "white" },
9
- secondary: {
10
- backgroundColor: "var(--color-border)",
11
- color: "var(--color-fg)",
12
- },
13
- };
14
-
15
- return (
16
- <button
17
- style={{
18
- padding: "8px 16px",
19
- borderRadius: "8px",
20
- fontWeight: 500,
21
- border: "none",
22
- cursor: "pointer",
23
- ...styles[variant],
24
- }}
25
- className={className}
26
- {...props}
27
- >
28
- {children}
29
- </button>
30
- );
31
- }