create-lumen 2.0.0-alpha.2 → 2.0.0-beta.1

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 (137) hide show
  1. package/README.md +24 -6
  2. package/bin/cli.js +2 -1
  3. package/package.json +2 -2
  4. package/src/cli-args.js +105 -5
  5. package/src/configure.js +8 -4
  6. package/src/dependencies.js +8 -2
  7. package/src/engine/capabilities.js +22 -1
  8. package/src/engine/composition.js +92 -0
  9. package/src/engine/providers/base-provider.js +4 -4
  10. package/src/main.js +33 -10
  11. package/src/manifest/emit.js +1 -1
  12. package/src/prompts.js +154 -23
  13. package/templates/bases/next/app-router/js/.provenance.json +19 -0
  14. package/templates/bases/next/app-router/js/README.md +36 -0
  15. package/templates/bases/next/app-router/js/jsconfig.json +7 -0
  16. package/templates/bases/next/app-router/js/next.config.mjs +6 -0
  17. package/templates/bases/next/app-router/js/package.json +15 -0
  18. package/templates/bases/next/app-router/js/public/file.svg +1 -0
  19. package/templates/bases/next/app-router/js/public/globe.svg +1 -0
  20. package/templates/bases/next/app-router/js/public/next.svg +1 -0
  21. package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
  22. package/templates/bases/next/app-router/js/public/window.svg +1 -0
  23. package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
  24. package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
  25. package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
  26. package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
  27. package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
  28. package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
  29. package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
  30. package/templates/bases/next/app-router/js/src/app/page.js +69 -0
  31. package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
  32. package/templates/bases/next/app-router/ts/.provenance.json +19 -0
  33. package/templates/bases/next/app-router/ts/README.md +36 -0
  34. package/templates/bases/next/app-router/ts/next.config.ts +7 -0
  35. package/templates/bases/next/app-router/ts/package.json +21 -0
  36. package/templates/bases/next/app-router/ts/public/file.svg +1 -0
  37. package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
  38. package/templates/bases/next/app-router/ts/public/next.svg +1 -0
  39. package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
  40. package/templates/bases/next/app-router/ts/public/window.svg +1 -0
  41. package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
  42. package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
  43. package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
  44. package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
  45. package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
  46. package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
  47. package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
  48. package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
  49. package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
  50. package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
  51. package/templates/bases/next/pages-router/js/.provenance.json +19 -0
  52. package/templates/bases/next/pages-router/js/README.md +40 -0
  53. package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
  54. package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
  55. package/templates/bases/next/pages-router/js/package.json +15 -0
  56. package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
  57. package/templates/bases/next/pages-router/js/public/file.svg +1 -0
  58. package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
  59. package/templates/bases/next/pages-router/js/public/next.svg +1 -0
  60. package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
  61. package/templates/bases/next/pages-router/js/public/window.svg +1 -0
  62. package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
  63. package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
  64. package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
  65. package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
  66. package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
  67. package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
  68. package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
  69. package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
  70. package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
  71. package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
  72. package/templates/bases/next/pages-router/ts/README.md +40 -0
  73. package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
  74. package/templates/bases/next/pages-router/ts/package.json +21 -0
  75. package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
  76. package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
  77. package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
  78. package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
  79. package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
  80. package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
  81. package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
  82. package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
  83. package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
  84. package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
  85. package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
  86. package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
  87. package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
  88. package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
  89. package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
  90. package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
  91. package/templates/conditional/linting/biome/biome.json +31 -0
  92. package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
  93. package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
  94. package/templates/conditional/next/config/node/next.config.mjs +6 -0
  95. package/templates/conditional/next/config/node/next.config.ts +7 -0
  96. package/templates/conditional/next/config/static/next.config.mjs +6 -0
  97. package/templates/conditional/next/config/static/next.config.ts +7 -0
  98. package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
  99. package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
  100. package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
  101. package/templates/conditional/next/config/vercel/next.config.ts +7 -0
  102. package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
  103. package/templates/conditional/next/config/webpack/next.config.ts +10 -0
  104. package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
  105. package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
  106. package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
  107. package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
  108. package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
  109. package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
  110. package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
  111. package/templates/conditional/next/font/src/app/layout.js +25 -0
  112. package/templates/conditional/next/font/src/app/layout.tsx +30 -0
  113. package/templates/conditional/next/font/src/lib/fonts.js +11 -0
  114. package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
  115. package/templates/conditional/next/font/src/pages/_app.js +20 -0
  116. package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
  117. package/templates/conditional/next/font/src/pages/_document.js +18 -0
  118. package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
  119. package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
  120. package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
  121. package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
  122. package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
  123. package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
  124. package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
  125. package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
  126. package/templates/conditional/next/image/src/components/image/index.js +1 -0
  127. package/templates/conditional/next/image/src/components/image/index.ts +2 -0
  128. package/templates/conditional/next/server-components/README.md +13 -0
  129. package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
  130. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
  131. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
  132. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
  133. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
  134. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
  135. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
  136. package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
  137. package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
@@ -0,0 +1,20 @@
1
+ import "@/styles/globals.css";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+
4
+ const geistSans = Geist({
5
+ variable: "--font-geist-sans",
6
+ subsets: ["latin"],
7
+ });
8
+
9
+ const geistMono = Geist_Mono({
10
+ variable: "--font-geist-mono",
11
+ subsets: ["latin"],
12
+ });
13
+
14
+ export default function App({ Component, pageProps }) {
15
+ return (
16
+ <div className={`${geistSans.variable} ${geistMono.variable}`}>
17
+ <Component {...pageProps} />
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,21 @@
1
+ import "@/styles/globals.css";
2
+ import type { AppProps } from "next/app";
3
+ import { Geist, Geist_Mono } from "next/font/google";
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ });
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ export default function App({ Component, pageProps }: AppProps) {
16
+ return (
17
+ <div className={`${geistSans.variable} ${geistMono.variable}`}>
18
+ <Component {...pageProps} />
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,18 @@
1
+ import { Html, Head, Main, NextScript } from "next/document";
2
+
3
+ /**
4
+ * Pages Router Custom Document.
5
+ * Note: Per Next.js compiler specifications, next/font loaders are configured
6
+ * in pages/_app to avoid the 'Cannot be used within pages/_document' compile error.
7
+ */
8
+ export default function Document() {
9
+ return (
10
+ <Html lang="en">
11
+ <Head />
12
+ <body className="antialiased">
13
+ <Main />
14
+ <NextScript />
15
+ </body>
16
+ </Html>
17
+ );
18
+ }
@@ -0,0 +1,18 @@
1
+ import { Html, Head, Main, NextScript } from "next/document";
2
+
3
+ /**
4
+ * Pages Router Custom Document.
5
+ * Note: Per Next.js compiler specifications, next/font loaders are configured
6
+ * in pages/_app to avoid the 'Cannot be used within pages/_document' compile error.
7
+ */
8
+ export default function Document() {
9
+ return (
10
+ <Html lang="en">
11
+ <Head />
12
+ <body className="antialiased">
13
+ <Main />
14
+ <NextScript />
15
+ </body>
16
+ </Html>
17
+ );
18
+ }
@@ -0,0 +1,25 @@
1
+ import { Geist, Geist_Mono } from "next/font/google";
2
+ import "./globals.css";
3
+
4
+ const geistSans = Geist({
5
+ variable: "--font-geist-sans",
6
+ subsets: ["latin"],
7
+ });
8
+
9
+ const geistMono = Geist_Mono({
10
+ variable: "--font-geist-mono",
11
+ subsets: ["latin"],
12
+ });
13
+
14
+ export const metadata = {
15
+ title: "Create Lumen App",
16
+ description: "Generated by create-lumen",
17
+ };
18
+
19
+ export default function RootLayout({ children }) {
20
+ return (
21
+ <html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
22
+ <body className="antialiased">{children}</body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,30 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ });
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ export const metadata: Metadata = {
16
+ title: "Create Lumen App",
17
+ description: "Generated by create-lumen",
18
+ };
19
+
20
+ export default function RootLayout({
21
+ children,
22
+ }: Readonly<{
23
+ children: React.ReactNode;
24
+ }>) {
25
+ return (
26
+ <html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
27
+ <body className="antialiased">{children}</body>
28
+ </html>
29
+ );
30
+ }
@@ -0,0 +1,11 @@
1
+ import { Geist, Geist_Mono } from "next/font/google";
2
+
3
+ export const geistSans = Geist({
4
+ variable: "--font-geist-sans",
5
+ subsets: ["latin"],
6
+ });
7
+
8
+ export const geistMono = Geist_Mono({
9
+ variable: "--font-geist-mono",
10
+ subsets: ["latin"],
11
+ });
@@ -0,0 +1,11 @@
1
+ import { Geist, Geist_Mono } from "next/font/google";
2
+
3
+ export const geistSans = Geist({
4
+ variable: "--font-geist-sans",
5
+ subsets: ["latin"],
6
+ });
7
+
8
+ export const geistMono = Geist_Mono({
9
+ variable: "--font-geist-mono",
10
+ subsets: ["latin"],
11
+ });
@@ -0,0 +1,20 @@
1
+ import "@/styles/globals.css";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+
4
+ const geistSans = Geist({
5
+ variable: "--font-geist-sans",
6
+ subsets: ["latin"],
7
+ });
8
+
9
+ const geistMono = Geist_Mono({
10
+ variable: "--font-geist-mono",
11
+ subsets: ["latin"],
12
+ });
13
+
14
+ export default function App({ Component, pageProps }) {
15
+ return (
16
+ <div className={`${geistSans.variable} ${geistMono.variable}`}>
17
+ <Component {...pageProps} />
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,21 @@
1
+ import "@/styles/globals.css";
2
+ import type { AppProps } from "next/app";
3
+ import { Geist, Geist_Mono } from "next/font/google";
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ });
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ export default function App({ Component, pageProps }: AppProps) {
16
+ return (
17
+ <div className={`${geistSans.variable} ${geistMono.variable}`}>
18
+ <Component {...pageProps} />
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,18 @@
1
+ import { Html, Head, Main, NextScript } from "next/document";
2
+
3
+ /**
4
+ * Pages Router Custom Document.
5
+ * Note: Per Next.js compiler specifications, next/font loaders are configured
6
+ * in pages/_app to avoid the 'Cannot be used within pages/_document' compile error.
7
+ */
8
+ export default function Document() {
9
+ return (
10
+ <Html lang="en">
11
+ <Head />
12
+ <body className="antialiased">
13
+ <Main />
14
+ <NextScript />
15
+ </body>
16
+ </Html>
17
+ );
18
+ }
@@ -0,0 +1,18 @@
1
+ import { Html, Head, Main, NextScript } from "next/document";
2
+
3
+ /**
4
+ * Pages Router Custom Document.
5
+ * Note: Per Next.js compiler specifications, next/font loaders are configured
6
+ * in pages/_app to avoid the 'Cannot be used within pages/_document' compile error.
7
+ */
8
+ export default function Document() {
9
+ return (
10
+ <Html lang="en">
11
+ <Head />
12
+ <body className="antialiased">
13
+ <Main />
14
+ <NextScript />
15
+ </body>
16
+ </Html>
17
+ );
18
+ }
@@ -0,0 +1,15 @@
1
+ # next/image Build Size and Performance Impact
2
+
3
+ ## Built-In Optimization
4
+ `next/image` extends standard HTML `<img>` elements with automatic visual stability and performance optimizations:
5
+ 1. **Zero Cumulative Layout Shift (CLS)**: Enforces explicit `width` and `height` dimensions or fills the parent container with `fill`, reserving geometric space before the bitmap payload loads.
6
+ 2. **Modern Format Negotiation**: Serves WebP and AVIF modern formats dynamically when supported by the requesting browser, yielding 30–50% bandwidth reduction compared to legacy JPEG/PNG.
7
+ 3. **Native Lazy Loading**: Viewport-aware on-demand loading so off-screen images do not delay initial page interactivity. The `priority` property allows opting into preloading for Largest Contentful Paint (LCP) elements.
8
+
9
+ ## Bundle Footprint
10
+ - **Runtime JavaScript Overhead**: ~5 KB gzipped client-side runtime handling IntersectionObserver and responsive `srcSet` generation.
11
+ - **Build Impact**: Zero additional build time overhead during static compilation when referencing local SVGs or remote URLs; image transformations are handled on-demand by the Next.js server image optimizer.
12
+
13
+ ## Router Parity
14
+ - **App Router**: Fully compatible with both Server Components (static rendering) and Client Components (`"use client"`).
15
+ - **Pages Router**: Fully compatible with all page and component hierarchies.
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ export function ImageExample({
5
+ src = "/next.svg",
6
+ alt = "Next.js logo",
7
+ width = 180,
8
+ height = 37,
9
+ priority = false,
10
+ className = "",
11
+ caption = "Optimized with next/image",
12
+ }) {
13
+ return (
14
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
15
+ <div className="relative flex items-center justify-center p-2">
16
+ <Image
17
+ src={src}
18
+ alt={alt}
19
+ width={width}
20
+ height={height}
21
+ priority={priority}
22
+ className="dark:invert transition-transform hover:scale-105"
23
+ />
24
+ </div>
25
+ {caption && (
26
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
27
+ {caption}
28
+ </figcaption>
29
+ )}
30
+ </figure>
31
+ );
32
+ }
33
+
34
+ export default ImageExample;
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ export interface ImageExampleProps {
5
+ src?: string;
6
+ alt?: string;
7
+ width?: number;
8
+ height?: number;
9
+ priority?: boolean;
10
+ className?: string;
11
+ caption?: string;
12
+ }
13
+
14
+ export function ImageExample({
15
+ src = "/next.svg",
16
+ alt = "Next.js logo",
17
+ width = 180,
18
+ height = 37,
19
+ priority = false,
20
+ className = "",
21
+ caption = "Optimized with next/image",
22
+ }: ImageExampleProps) {
23
+ return (
24
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
25
+ <div className="relative flex items-center justify-center p-2">
26
+ <Image
27
+ src={src}
28
+ alt={alt}
29
+ width={width}
30
+ height={height}
31
+ priority={priority}
32
+ className="dark:invert transition-transform hover:scale-105"
33
+ />
34
+ </div>
35
+ {caption && (
36
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
37
+ {caption}
38
+ </figcaption>
39
+ )}
40
+ </figure>
41
+ );
42
+ }
43
+
44
+ export default ImageExample;
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ export function ImageExample({
5
+ src = "/next.svg",
6
+ alt = "Next.js logo",
7
+ width = 180,
8
+ height = 37,
9
+ priority = false,
10
+ className = "",
11
+ caption = "Optimized with next/image",
12
+ }) {
13
+ return (
14
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
15
+ <div className="relative flex items-center justify-center p-2">
16
+ <Image
17
+ src={src}
18
+ alt={alt}
19
+ width={width}
20
+ height={height}
21
+ priority={priority}
22
+ className="dark:invert transition-transform hover:scale-105"
23
+ />
24
+ </div>
25
+ {caption && (
26
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
27
+ {caption}
28
+ </figcaption>
29
+ )}
30
+ </figure>
31
+ );
32
+ }
33
+
34
+ export default ImageExample;
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ export interface ImageExampleProps {
5
+ src?: string;
6
+ alt?: string;
7
+ width?: number;
8
+ height?: number;
9
+ priority?: boolean;
10
+ className?: string;
11
+ caption?: string;
12
+ }
13
+
14
+ export function ImageExample({
15
+ src = "/next.svg",
16
+ alt = "Next.js logo",
17
+ width = 180,
18
+ height = 37,
19
+ priority = false,
20
+ className = "",
21
+ caption = "Optimized with next/image",
22
+ }: ImageExampleProps) {
23
+ return (
24
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
25
+ <div className="relative flex items-center justify-center p-2">
26
+ <Image
27
+ src={src}
28
+ alt={alt}
29
+ width={width}
30
+ height={height}
31
+ priority={priority}
32
+ className="dark:invert transition-transform hover:scale-105"
33
+ />
34
+ </div>
35
+ {caption && (
36
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
37
+ {caption}
38
+ </figcaption>
39
+ )}
40
+ </figure>
41
+ );
42
+ }
43
+
44
+ export default ImageExample;
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ /**
5
+ * Universal Next.js Image component example.
6
+ * Works seamlessly in both App Router (Server and Client components)
7
+ * and Pages Router.
8
+ *
9
+ * Demonstrates:
10
+ * - Built-in automatic format optimization (WebP/AVIF)
11
+ * - Intrinsic sizing to avoid layout shift (CLS)
12
+ * - Priority loading option for LCP optimization
13
+ */
14
+ export function ImageExample({
15
+ src = "/next.svg",
16
+ alt = "Next.js logo",
17
+ width = 180,
18
+ height = 37,
19
+ priority = false,
20
+ className = "",
21
+ caption = "Optimized with next/image",
22
+ }) {
23
+ return (
24
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
25
+ <div className="relative flex items-center justify-center p-2">
26
+ <Image
27
+ src={src}
28
+ alt={alt}
29
+ width={width}
30
+ height={height}
31
+ priority={priority}
32
+ className="dark:invert transition-transform hover:scale-105"
33
+ />
34
+ </div>
35
+ {caption && (
36
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
37
+ {caption}
38
+ </figcaption>
39
+ )}
40
+ </figure>
41
+ );
42
+ }
43
+
44
+ export default ImageExample;
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+
4
+ export interface ImageExampleProps {
5
+ src?: string;
6
+ alt?: string;
7
+ width?: number;
8
+ height?: number;
9
+ priority?: boolean;
10
+ className?: string;
11
+ caption?: string;
12
+ }
13
+
14
+ /**
15
+ * Universal Next.js Image component example.
16
+ * Works seamlessly in both App Router (Server and Client components)
17
+ * and Pages Router.
18
+ *
19
+ * Demonstrates:
20
+ * - Built-in automatic format optimization (WebP/AVIF)
21
+ * - Intrinsic sizing to avoid layout shift (CLS)
22
+ * - Priority loading option for LCP optimization
23
+ */
24
+ export function ImageExample({
25
+ src = "/next.svg",
26
+ alt = "Next.js logo",
27
+ width = 180,
28
+ height = 37,
29
+ priority = false,
30
+ className = "",
31
+ caption = "Optimized with next/image",
32
+ }: ImageExampleProps) {
33
+ return (
34
+ <figure className={`inline-flex flex-col items-center gap-2 p-4 rounded-xl border border-gray-200 dark:border-gray-800 bg-white/50 dark:bg-black/20 ${className}`}>
35
+ <div className="relative flex items-center justify-center p-2">
36
+ <Image
37
+ src={src}
38
+ alt={alt}
39
+ width={width}
40
+ height={height}
41
+ priority={priority}
42
+ className="dark:invert transition-transform hover:scale-105"
43
+ />
44
+ </div>
45
+ {caption && (
46
+ <figcaption className="text-xs text-gray-500 dark:text-gray-400 text-center font-mono">
47
+ {caption}
48
+ </figcaption>
49
+ )}
50
+ </figure>
51
+ );
52
+ }
53
+
54
+ export default ImageExample;
@@ -0,0 +1 @@
1
+ export { ImageExample } from "../ImageExample";
@@ -0,0 +1,2 @@
1
+ export { ImageExample } from "../ImageExample";
2
+ export type { ImageExampleProps } from "../ImageExample";
@@ -0,0 +1,13 @@
1
+ # React Server Components (RSC) Boundaries & Linting
2
+
3
+ This conditional template provides:
4
+ 1. **Server vs Client Boundary Components**: Clean demonstrations of React Server Components composing Client Components with `"use client"`.
5
+ 2. **ESLint Rule Wiring (`rsc/no-server-imports-in-client`)**: An AST-based ESLint rule that disallows importing server-only packages (e.g., `server-only`, Node.js built-ins) into Client Components marked with `"use client"`.
6
+ 3. **Dual Router Compatibility**: Operates seamlessly in both Next.js App Router and Pages Router.
7
+
8
+ ## Components
9
+ - `ClientCounter`: An interactive client component demonstrating React state and event handling.
10
+ - `ServerCard`: A server-rendered component shell passing children and composing `ClientCounter` across the boundary.
11
+
12
+ ## ESLint Configuration
13
+ Wires `rsc/no-server-imports-in-client` and `no-restricted-imports` into Next.js flat configuration (`eslint.config.mjs`).
@@ -0,0 +1,47 @@
1
+ import { noServerImportsInClient } from "./rules/no-server-imports-in-client.mjs";
2
+
3
+ let baseConfig = [];
4
+ try {
5
+ const next = await import("eslint-config-next");
6
+ const imported = next.default || next;
7
+ baseConfig = Array.isArray(imported) ? imported : [imported];
8
+ } catch {
9
+ try {
10
+ const { FlatCompat } = await import("@eslint/eslintrc");
11
+ const { fileURLToPath } = await import("url");
12
+ const { dirname } = await import("path");
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const compat = new FlatCompat({ baseDirectory: __dirname });
16
+ baseConfig = compat.extends("next/core-web-vitals");
17
+ } catch {}
18
+ }
19
+
20
+ const eslintConfig = [
21
+ ...baseConfig,
22
+ {
23
+ plugins: {
24
+ rsc: {
25
+ rules: {
26
+ "no-server-imports-in-client": noServerImportsInClient,
27
+ },
28
+ },
29
+ },
30
+ rules: {
31
+ "rsc/no-server-imports-in-client": "error",
32
+ "no-restricted-imports": [
33
+ "error",
34
+ {
35
+ patterns: [
36
+ {
37
+ group: ["server-only", "**/server-only/**"],
38
+ message: "Do not import server-only packages in client modules.",
39
+ },
40
+ ],
41
+ },
42
+ ],
43
+ },
44
+ },
45
+ ];
46
+
47
+ export default eslintConfig;