sleeke 1.0.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.
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/next-app/.claude/docs/conventional-commits.md +149 -0
- package/dist/templates/next-app/.claude/skills/git-commiter/SKILL.md +411 -0
- package/dist/templates/next-app/.claude/skills/translate-pages/SKILL.md +15 -0
- package/dist/templates/next-app/.claude/skills/translate-pages/scripts/translate.ts +0 -0
- package/dist/templates/next-app/.prettierignore +7 -0
- package/dist/templates/next-app/.prettierrc +11 -0
- package/dist/templates/next-app/CLAUDE.md +19 -0
- package/dist/templates/next-app/eslint.config.mjs +18 -0
- package/dist/templates/next-app/global.d.ts +8 -0
- package/dist/templates/next-app/luxw.json +12 -0
- package/dist/templates/next-app/next.config.ts +9 -0
- package/dist/templates/next-app/package.json +35 -0
- package/dist/templates/next-app/postcss.config.mjs +7 -0
- package/dist/templates/next-app/src/app/[locale]/[...rest]/page.tsx +5 -0
- package/dist/templates/next-app/src/app/[locale]/error.tsx +24 -0
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +76 -0
- package/dist/templates/next-app/src/app/[locale]/locale-not-supported/page.tsx +49 -0
- package/dist/templates/next-app/src/app/[locale]/not-found.tsx +23 -0
- package/dist/templates/next-app/src/app/[locale]/page.tsx +24 -0
- package/dist/templates/next-app/src/app/favicon.ico +0 -0
- package/dist/templates/next-app/src/components/theme-hotkey.tsx +28 -0
- package/dist/templates/next-app/src/i18n/load-messages.ts +65 -0
- package/dist/templates/next-app/src/i18n/navigation.ts +5 -0
- package/dist/templates/next-app/src/i18n/request.ts +32 -0
- package/dist/templates/next-app/src/i18n/routing.ts +11 -0
- package/dist/templates/next-app/src/proxy.ts +55 -0
- package/dist/templates/next-app/src/styles/globals.css +88 -0
- package/dist/templates/next-app/tsconfig.json +34 -0
- package/package.json +68 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "next dev --turbopack",
|
|
8
|
+
"build": "next build",
|
|
9
|
+
"start": "next start",
|
|
10
|
+
"lint": "eslint",
|
|
11
|
+
"format": "prettier --write \"**/*.{ts,tsx}\"",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"next": "^16.2.9",
|
|
16
|
+
"next-intl": "^4.13.0",
|
|
17
|
+
"@wrksz/themes": "^1.0.0",
|
|
18
|
+
"react": "^19.2.4",
|
|
19
|
+
"react-dom": "^19.2.4"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@eslint/eslintrc": "^3",
|
|
23
|
+
"@tailwindcss/postcss": "^4.2.1",
|
|
24
|
+
"@types/node": "^25.5.0",
|
|
25
|
+
"@types/react": "^19.2.14",
|
|
26
|
+
"@types/react-dom": "^19.2.3",
|
|
27
|
+
"eslint": "^9.39.4",
|
|
28
|
+
"eslint-config-next": "^16.2.9",
|
|
29
|
+
"postcss": "^8.5.10",
|
|
30
|
+
"prettier": "^3.8.1",
|
|
31
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
32
|
+
"tailwindcss": "^4.2.1",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
export default function ErrorPage({ error, reset }: { error: Error & { digest?: string }; reset: () => void; }) {
|
|
7
|
+
const t = useTranslations("errors");
|
|
8
|
+
const common = useTranslations("common");
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
console.error("Application error:", error);
|
|
12
|
+
document.title = `${t("internal.title")} | ${common("meta.site-name")}`;
|
|
13
|
+
}, [error]);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<h1>{t("internal.title")}</h1>
|
|
18
|
+
<p>{t("internal.description")}</p>
|
|
19
|
+
<a onClick={() => reset()} className="text-blue-500 cursor-pointer hover:underline">
|
|
20
|
+
{t("internal.try-again")}
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { NextIntlClientProvider } from 'next-intl';
|
|
2
|
+
import { ThemeProvider } from '@wrksz/themes/next';
|
|
3
|
+
import { ThemeHotkey } from '@/components/theme-hotkey';
|
|
4
|
+
import { routing } from '@/i18n/routing';
|
|
5
|
+
import { hasLocale } from 'next-intl';
|
|
6
|
+
import { getMessages, getTranslations } from 'next-intl/server';
|
|
7
|
+
import { notFound } from 'next/navigation';
|
|
8
|
+
import type { Metadata } from 'next';
|
|
9
|
+
import '@/styles/globals.css';
|
|
10
|
+
|
|
11
|
+
const baseUrl = "https://example.com";
|
|
12
|
+
|
|
13
|
+
type Props = { children: React.ReactNode; params: Promise<{locale: string}>; };
|
|
14
|
+
|
|
15
|
+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
16
|
+
const { locale } = await params;
|
|
17
|
+
const t = await getTranslations({ locale, namespace: "common" });
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
title: t("meta.site-name"),
|
|
21
|
+
description: t("meta.description"),
|
|
22
|
+
keywords: t("meta.keywords"),
|
|
23
|
+
robots: { index: true, follow: true },
|
|
24
|
+
metadataBase: new URL(baseUrl),
|
|
25
|
+
alternates: {
|
|
26
|
+
canonical: baseUrl,
|
|
27
|
+
},
|
|
28
|
+
openGraph: {
|
|
29
|
+
title: t("meta.site-name"),
|
|
30
|
+
description: t("meta.description"),
|
|
31
|
+
url: baseUrl,
|
|
32
|
+
siteName: t("meta.site-name"),
|
|
33
|
+
locale,
|
|
34
|
+
type: "website",
|
|
35
|
+
images: [{ url: t("meta.openGraph.image"), width: 1200, height: 630 }],
|
|
36
|
+
},
|
|
37
|
+
twitter: {
|
|
38
|
+
card: t("meta.twitter.card") as "summary_large_image",
|
|
39
|
+
site: t("meta.twitter.profile"),
|
|
40
|
+
title: t("meta.site-name"),
|
|
41
|
+
description: t("meta.description"),
|
|
42
|
+
images: [t("meta.openGraph.image")],
|
|
43
|
+
},
|
|
44
|
+
icons: {
|
|
45
|
+
icon: "/favicon.ico",
|
|
46
|
+
apple: "/apple-touch-icon.png",
|
|
47
|
+
},
|
|
48
|
+
other: {
|
|
49
|
+
"color-scheme": "light dark",
|
|
50
|
+
"googlebot": "index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default async function RootLayout({children, params}: Props) {
|
|
56
|
+
const { locale } = await params;
|
|
57
|
+
|
|
58
|
+
if (!hasLocale(routing.locales, locale)) {
|
|
59
|
+
notFound();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const messages = await getMessages();
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<html lang = { locale } suppressHydrationWarning>
|
|
66
|
+
<body>
|
|
67
|
+
<NextIntlClientProvider messages={messages}>
|
|
68
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange themeColor={{ light: "#ffffff", dark: "#0a0a0a" }}>
|
|
69
|
+
<ThemeHotkey />
|
|
70
|
+
{ children }
|
|
71
|
+
</ThemeProvider>
|
|
72
|
+
</NextIntlClientProvider>
|
|
73
|
+
</body>
|
|
74
|
+
</html>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useTranslations } from "next-intl";
|
|
2
|
+
import { getTranslations } from "next-intl/server";
|
|
3
|
+
import { redirect } from "next/navigation";
|
|
4
|
+
import luxm from "../../../../luxm.json";
|
|
5
|
+
|
|
6
|
+
const supportedLocales = new Set<string>(luxm.i18n.supportedLocales);
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
params: Promise<{ locale: string }>;
|
|
10
|
+
searchParams: Promise<{ locale?: string; path?: string }>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function generateMetadata({ params }: Props) {
|
|
14
|
+
const { locale } = await params;
|
|
15
|
+
const t = await getTranslations({ locale, namespace: "errors" });
|
|
16
|
+
const common = await getTranslations({ locale, namespace: "common" });
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
title: `${t("locale-not-supported.title")} | ${common("meta.site-name")}`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default async function LocaleNotSupportedPage({ searchParams, params }: Props) {
|
|
24
|
+
const { locale: requestedLocale, path: originalPath } = await searchParams;
|
|
25
|
+
const { locale } = await params;
|
|
26
|
+
|
|
27
|
+
// Se o idioma do query param já é suportado, redireciona para a página original
|
|
28
|
+
if (requestedLocale && supportedLocales.has(requestedLocale)) {
|
|
29
|
+
redirect(`/${requestedLocale}${originalPath ?? ''}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const backHref = originalPath ? `/${locale}${originalPath}` : `/${locale}`;
|
|
33
|
+
|
|
34
|
+
return <LocaleNotSupportedContent backHref={backHref} />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function LocaleNotSupportedContent({ backHref }: { backHref: string }) {
|
|
38
|
+
const t = useTranslations("errors");
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
<h1>{t("locale-not-supported.title")}</h1>
|
|
43
|
+
<p>{t("locale-not-supported.description")}</p>
|
|
44
|
+
<a href={backHref} className="text-blue-500 cursor-pointer hover:underline">
|
|
45
|
+
{t("locale-not-supported.action")}
|
|
46
|
+
</a>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
export default function NotFound() {
|
|
7
|
+
const t = useTranslations("errors");
|
|
8
|
+
const common = useTranslations("common");
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
document.title = `${t("not-found.title")} | ${common("meta.site-name")}`;
|
|
12
|
+
}, []);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div>
|
|
16
|
+
<h1>{t("not-found.title")}</h1>
|
|
17
|
+
<p>{t("not-found.description")}</p>
|
|
18
|
+
<a href="/" className="text-blue-500 cursor-pointer hover:underline">
|
|
19
|
+
{t("not-found.action")}
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useTranslations } from "next-intl";
|
|
2
|
+
import { getTranslations } from "next-intl/server";
|
|
3
|
+
import type { Metadata } from "next";
|
|
4
|
+
|
|
5
|
+
type Props = { params: Promise<{ locale: string }> };
|
|
6
|
+
|
|
7
|
+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
8
|
+
const { locale } = await params;
|
|
9
|
+
const t = await getTranslations({ locale, namespace: "home" });
|
|
10
|
+
const common = await getTranslations({ locale, namespace: "common" });
|
|
11
|
+
return {
|
|
12
|
+
title: `${t("meta.title")} | ${common("meta.site-name")}`,
|
|
13
|
+
description: t("meta.description"),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function HomePage() {
|
|
18
|
+
const t = useTranslations("home");
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
<h1>{t("page.title")}</h1>
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTheme } from "@wrksz/themes/client";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
function isTypingTarget(target: EventTarget | null) {
|
|
7
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
8
|
+
return target.isContentEditable || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ThemeHotkey() {
|
|
12
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
function onKeyDown(event: KeyboardEvent) {
|
|
16
|
+
if (event.defaultPrevented || event.repeat) return;
|
|
17
|
+
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
18
|
+
if (event.key.toLowerCase() !== "d") return;
|
|
19
|
+
if (isTypingTarget(event.target)) return;
|
|
20
|
+
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
window.addEventListener("keydown", onKeyDown);
|
|
24
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
25
|
+
}, [resolvedTheme, setTheme]);
|
|
26
|
+
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import config from "../../luxw.json";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
type Messages = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
const cache = new Map<string, Messages>();
|
|
9
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
10
|
+
|
|
11
|
+
export async function loadMessages(locale: string): Promise<Messages> {
|
|
12
|
+
return isProd
|
|
13
|
+
? loadProductionMessages(locale)
|
|
14
|
+
: loadDevelopmentMessages(locale);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function loadProductionMessages(locale: string): Promise<Messages> {
|
|
18
|
+
const cached = cache.get(locale);
|
|
19
|
+
if (cached) return cached;
|
|
20
|
+
|
|
21
|
+
const filePath = path.join(process.cwd(), "dist/i18n", `${locale}.json`);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const messages = await readJson(filePath);
|
|
25
|
+
cache.set(locale, messages);
|
|
26
|
+
return messages;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Failed to initialize application in production.\nMissing localization bundles — run the build before starting the production server.`,
|
|
30
|
+
{ cause: error }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function loadDevelopmentMessages(locale: string): Promise<Messages> {
|
|
36
|
+
const localeDirectory = config.i18n?.localeDirectory ?? "src/locales";
|
|
37
|
+
const dir = path.join(process.cwd(), localeDirectory, locale);
|
|
38
|
+
|
|
39
|
+
if (!existsSync(dir)) return {};
|
|
40
|
+
|
|
41
|
+
const messages: Messages = {};
|
|
42
|
+
await collectJsonFiles(dir, messages);
|
|
43
|
+
return messages;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function collectJsonFiles(dir: string, target: Messages): Promise<void> {
|
|
47
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
48
|
+
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const fullPath = path.join(dir, entry.name);
|
|
51
|
+
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
await collectJsonFiles(fullPath, target);
|
|
54
|
+
} else if (entry.name.endsWith(".json")) {
|
|
55
|
+
const content = await readJson(fullPath);
|
|
56
|
+
Object.assign(target, content);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function readJson(filePath: string): Promise<Messages> {
|
|
62
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
63
|
+
if (!raw.trim()) return {};
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
2
|
+
import { hasLocale } from 'next-intl';
|
|
3
|
+
import { routing } from './routing';
|
|
4
|
+
import { loadMessages } from './load-messages';
|
|
5
|
+
|
|
6
|
+
export default getRequestConfig(async ({ requestLocale }) => {
|
|
7
|
+
const requested = await requestLocale;
|
|
8
|
+
|
|
9
|
+
const locale =
|
|
10
|
+
typeof requested === 'string' && hasLocale(routing.locales, requested)
|
|
11
|
+
? requested
|
|
12
|
+
: routing.defaultLocale;
|
|
13
|
+
|
|
14
|
+
// Carrega as mensagens do idioma atual
|
|
15
|
+
const messages = await loadMessages(locale);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
locale,
|
|
19
|
+
messages,
|
|
20
|
+
getMessageFallback({ namespace, key, error }) {
|
|
21
|
+
const path = [namespace, key].filter((part) => part != null).join('.');
|
|
22
|
+
|
|
23
|
+
if (error.code === 'MISSING_MESSAGE') {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`[i18n] Tradução obrigatória faltando: "${path}" no idioma "${locale}".`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineRouting } from "next-intl/routing";
|
|
2
|
+
import luxw from "../../luxw.json";
|
|
3
|
+
|
|
4
|
+
export const routing = defineRouting({
|
|
5
|
+
locales: luxw.i18n.locales as [string, ...string[]],
|
|
6
|
+
defaultLocale: luxw.i18n.defaultLocale,
|
|
7
|
+
localePrefix: "always",
|
|
8
|
+
pathnames: {
|
|
9
|
+
"/": "/"
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import createMiddleware from 'next-intl/middleware';
|
|
2
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
3
|
+
import { routing } from './i18n/routing';
|
|
4
|
+
import luxw from '../luxw.json';
|
|
5
|
+
|
|
6
|
+
const intlMiddleware = createMiddleware(routing);
|
|
7
|
+
|
|
8
|
+
const allLocales = new Set<string>(luxw.i18n.locales);
|
|
9
|
+
const supportedLocales = new Set<string>(luxw.i18n.supportedLocales);
|
|
10
|
+
const defaultLocale = luxw.i18n.defaultLocale;
|
|
11
|
+
const knownRoutes = new Set<string>([
|
|
12
|
+
...(luxw.routes.publicRoutes as string[]).map(r => r.startsWith('/') ? r : `/${r}`),
|
|
13
|
+
...(luxw.routes.privateRoutes as string[]).map(r => r.startsWith('/') ? r : `/${r}`),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export default function middleware(request: NextRequest) {
|
|
17
|
+
const segments = request.nextUrl.pathname.split('/').filter(Boolean);
|
|
18
|
+
const locale = segments[0] ?? '';
|
|
19
|
+
const routePath = '/' + segments.slice(1).join('/');
|
|
20
|
+
|
|
21
|
+
if (locale && allLocales.has(locale) && !supportedLocales.has(locale)) {
|
|
22
|
+
const routeExists = knownRoutes.has(routePath);
|
|
23
|
+
|
|
24
|
+
if (!routeExists) {
|
|
25
|
+
const response = intlMiddleware(request);
|
|
26
|
+
const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value;
|
|
27
|
+
const safeLocale = cookieLocale && supportedLocales.has(cookieLocale)
|
|
28
|
+
? cookieLocale
|
|
29
|
+
: defaultLocale;
|
|
30
|
+
response.cookies.set('NEXT_LOCALE', safeLocale);
|
|
31
|
+
return response;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value;
|
|
35
|
+
const fallbackLocale = cookieLocale && supportedLocales.has(cookieLocale)
|
|
36
|
+
? cookieLocale
|
|
37
|
+
: defaultLocale;
|
|
38
|
+
|
|
39
|
+
const url = request.nextUrl.clone();
|
|
40
|
+
url.pathname = `/${fallbackLocale}/locale-not-supported`;
|
|
41
|
+
url.searchParams.set('locale', locale);
|
|
42
|
+
|
|
43
|
+
if (routePath !== '/') {
|
|
44
|
+
url.searchParams.set('path', routePath);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return NextResponse.redirect(url);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return intlMiddleware(request);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const config = {
|
|
54
|
+
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
|
|
55
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@custom-variant dark (&:is(.dark *));
|
|
4
|
+
|
|
5
|
+
@theme inline {
|
|
6
|
+
--color-background: var(--background);
|
|
7
|
+
--color-foreground: var(--foreground);
|
|
8
|
+
--color-card: var(--card);
|
|
9
|
+
--color-card-foreground: var(--card-foreground);
|
|
10
|
+
--color-popover: var(--popover);
|
|
11
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
12
|
+
--color-primary: var(--primary);
|
|
13
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
14
|
+
--color-secondary: var(--secondary);
|
|
15
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
16
|
+
--color-muted: var(--muted);
|
|
17
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
18
|
+
--color-accent: var(--accent);
|
|
19
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
20
|
+
--color-destructive: var(--destructive);
|
|
21
|
+
--color-border: var(--border);
|
|
22
|
+
--color-input: var(--input);
|
|
23
|
+
--color-ring: var(--ring);
|
|
24
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
25
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
26
|
+
--radius-lg: var(--radius);
|
|
27
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
28
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
29
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
30
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:root {
|
|
34
|
+
--background: oklch(1 0 0);
|
|
35
|
+
--foreground: oklch(0.145 0 0);
|
|
36
|
+
--card: oklch(1 0 0);
|
|
37
|
+
--card-foreground: oklch(0.145 0 0);
|
|
38
|
+
--popover: oklch(1 0 0);
|
|
39
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
40
|
+
--primary: oklch(0.205 0 0);
|
|
41
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
42
|
+
--secondary: oklch(0.97 0 0);
|
|
43
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
44
|
+
--muted: oklch(0.97 0 0);
|
|
45
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
46
|
+
--accent: oklch(0.97 0 0);
|
|
47
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
48
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
49
|
+
--border: oklch(0.922 0 0);
|
|
50
|
+
--input: oklch(0.922 0 0);
|
|
51
|
+
--ring: oklch(0.708 0 0);
|
|
52
|
+
--radius: 0.625rem;
|
|
53
|
+
color-scheme: light;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dark {
|
|
57
|
+
--background: oklch(0.145 0 0);
|
|
58
|
+
--foreground: oklch(0.985 0 0);
|
|
59
|
+
--card: oklch(0.205 0 0);
|
|
60
|
+
--card-foreground: oklch(0.985 0 0);
|
|
61
|
+
--popover: oklch(0.205 0 0);
|
|
62
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
63
|
+
--primary: oklch(0.922 0 0);
|
|
64
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
65
|
+
--secondary: oklch(0.269 0 0);
|
|
66
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
67
|
+
--muted: oklch(0.269 0 0);
|
|
68
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
69
|
+
--accent: oklch(0.269 0 0);
|
|
70
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
71
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
72
|
+
--border: oklch(1 0 0 / 10%);
|
|
73
|
+
--input: oklch(1 0 0 / 15%);
|
|
74
|
+
--ring: oklch(0.556 0 0);
|
|
75
|
+
color-scheme: dark;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@layer base {
|
|
79
|
+
* {
|
|
80
|
+
@apply border-border outline-ring/50;
|
|
81
|
+
}
|
|
82
|
+
body {
|
|
83
|
+
@apply bg-background text-foreground;
|
|
84
|
+
}
|
|
85
|
+
::selection {
|
|
86
|
+
@apply bg-primary text-primary-foreground;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [
|
|
17
|
+
{
|
|
18
|
+
"name": "next"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"baseUrl": ".",
|
|
22
|
+
"paths": {
|
|
23
|
+
"@/*": ["./src/*"]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"include": [
|
|
27
|
+
"next-env.d.ts",
|
|
28
|
+
"**/*.ts",
|
|
29
|
+
"**/*.tsx",
|
|
30
|
+
".next/types/**/*.ts",
|
|
31
|
+
".next/dev/types/**/*.ts"
|
|
32
|
+
],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sleeke",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Nextjs template generator",
|
|
5
|
+
"author": "araujoio",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/araujoio/luxw.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"luxw",
|
|
13
|
+
"cli",
|
|
14
|
+
"nextjs",
|
|
15
|
+
"react",
|
|
16
|
+
"typescript",
|
|
17
|
+
"tailwind"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"luxw": "dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "tsup --watch",
|
|
35
|
+
"build": "tsup --clean && tsx src/utils/copy-templates.ts",
|
|
36
|
+
"start": "node dist/index.js",
|
|
37
|
+
"clean": "rimraf dist",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
40
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
41
|
+
"test": "vitest"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@clack/prompts": "^1.6.0",
|
|
45
|
+
"chalk": "5.6.2",
|
|
46
|
+
"commander": "14.0.3",
|
|
47
|
+
"execa": "9.6.1",
|
|
48
|
+
"fs-extra": "11.3.4",
|
|
49
|
+
"kleur": "4.1.5",
|
|
50
|
+
"ora": "9.3.0",
|
|
51
|
+
"prompts": "2.4.2",
|
|
52
|
+
"zod": "4.3.6"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^10.0.1",
|
|
56
|
+
"@types/fs-extra": "11.0.4",
|
|
57
|
+
"@types/node": "25.5.2",
|
|
58
|
+
"eslint": "^10.2.0",
|
|
59
|
+
"eslint-config-prettier": "^10.1.8",
|
|
60
|
+
"prettier": "^3.8.2",
|
|
61
|
+
"rimraf": "6.1.3",
|
|
62
|
+
"tsup": "8.5.1",
|
|
63
|
+
"tsx": "4.21.0",
|
|
64
|
+
"typescript": "6.0.2",
|
|
65
|
+
"typescript-eslint": "^8.58.1",
|
|
66
|
+
"vitest": "^4.1.4"
|
|
67
|
+
}
|
|
68
|
+
}
|