nuxt-bake 1.0.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 (66) hide show
  1. package/README.md +79 -0
  2. package/dist/helpers/constants.js +28 -0
  3. package/dist/helpers/git.js +32 -0
  4. package/dist/helpers/package-manager.js +77 -0
  5. package/dist/helpers/template.js +27 -0
  6. package/dist/helpers/utils.js +42 -0
  7. package/dist/index.js +96 -0
  8. package/eslint.config.ts +26 -0
  9. package/package.json +58 -0
  10. package/playwright.config.ts +6 -0
  11. package/src/helpers/git.ts +28 -0
  12. package/src/helpers/package-manager.ts +86 -0
  13. package/src/helpers/template.ts +35 -0
  14. package/src/helpers/utils.ts +83 -0
  15. package/src/index.ts +106 -0
  16. package/templates/base/.env.example +11 -0
  17. package/templates/base/README.md +58 -0
  18. package/templates/base/app/app.vue +20 -0
  19. package/templates/base/app/assets/styles.css +257 -0
  20. package/templates/base/app/components/dialog.vue +54 -0
  21. package/templates/base/app/components/navbar.vue +51 -0
  22. package/templates/base/app/components/toast.vue +116 -0
  23. package/templates/base/app/composables/use-session-monitor.ts +40 -0
  24. package/templates/base/app/composables/use-theme.ts +41 -0
  25. package/templates/base/app/composables/use-toast.ts +37 -0
  26. package/templates/base/app/error.vue +23 -0
  27. package/templates/base/app/layouts/default.vue +7 -0
  28. package/templates/base/app/pages/index.vue +53 -0
  29. package/templates/base/app/pages/sign-in.vue +39 -0
  30. package/templates/base/app/stores/user-store.ts +50 -0
  31. package/templates/base/app/utils/helpers.ts +42 -0
  32. package/templates/base/eslint.config.ts +74 -0
  33. package/templates/base/nuxt.config.ts +39 -0
  34. package/templates/base/package.json +39 -0
  35. package/templates/base/prisma/schema.prisma +30 -0
  36. package/templates/base/prisma.config.ts +12 -0
  37. package/templates/base/server/api/auth/[provider].ts +67 -0
  38. package/templates/base/server/api/user/index.delete.ts +8 -0
  39. package/templates/base/server/api/user/index.get.ts +10 -0
  40. package/templates/base/server/utils/auth.ts +48 -0
  41. package/templates/base/server/utils/db.ts +15 -0
  42. package/templates/base/server/utils/helpers.ts +14 -0
  43. package/templates/base/shared/types/auth.d.ts +31 -0
  44. package/templates/base/shared/types/globals.d.ts +15 -0
  45. package/templates/base/tsconfig.json +18 -0
  46. package/templates/with-i18n/app/app.vue +29 -0
  47. package/templates/with-i18n/app/components/navbar.vue +74 -0
  48. package/templates/with-i18n/app/error.vue +25 -0
  49. package/templates/with-i18n/app/pages/index.vue +53 -0
  50. package/templates/with-i18n/app/pages/sign-in.vue +39 -0
  51. package/templates/with-i18n/app/utils/i18n.config.ts +14 -0
  52. package/templates/with-i18n/app/utils/locales/en-US.json +50 -0
  53. package/templates/with-i18n/app/utils/locales/fr-FR.json +50 -0
  54. package/templates/with-i18n/nuxt.config.ts +51 -0
  55. package/templates/with-tests/app/pages/index.vue +51 -0
  56. package/templates/with-tests/nuxt.config.ts +31 -0
  57. package/templates/with-tests/playwright.config.ts +13 -0
  58. package/templates/with-tests/tests/e2e/e2e-hello.test.ts +8 -0
  59. package/templates/with-tests/tests/hello.test.ts +7 -0
  60. package/templates/with-tests/vitest.config.ts +16 -0
  61. package/tests/git.test.ts +54 -0
  62. package/tests/package-manager.test.ts +100 -0
  63. package/tests/template.test.ts +73 -0
  64. package/tests/utils.test.ts +155 -0
  65. package/tsconfig.json +13 -0
  66. package/vitest.config.ts +15 -0
@@ -0,0 +1,67 @@
1
+ import type { EventHandlerRequest, H3Event } from "h3"
2
+
3
+ const configs: Record<string, any> = {
4
+ google: {
5
+ clientId: process.env.NUXT_OAUTH_GOOGLE_CLIENT_ID,
6
+ clientSecret: process.env.NUXT_OAUTH_GOOGLE_CLIENT_SECRET,
7
+ redirectURL: `${process.env.NUXT_PUBLIC_BASE_URL}/api/auth/google`,
8
+ },
9
+ github: {
10
+ emailRequired: true,
11
+ clientId: process.env.NUXT_OAUTH_GITHUB_CLIENT_ID,
12
+ clientSecret: process.env.NUXT_OAUTH_GITHUB_CLIENT_SECRET,
13
+ redirectURL: `${process.env.NUXT_PUBLIC_BASE_URL}/api/auth/github`,
14
+ },
15
+ }
16
+
17
+ const extractUserData: Record<string, (user: any) => any> = {
18
+ google: user => ({
19
+ id: user.id?.toString() || user.sub?.toString(),
20
+ name: user.name || user.given_name,
21
+ email: user.email,
22
+ image: user.picture,
23
+ }),
24
+ github: user => ({
25
+ id: user.id?.toString(),
26
+ name: user.name,
27
+ email: user.email,
28
+ image: user.avatar_url,
29
+ }),
30
+ }
31
+
32
+ export default defineEventHandler(async (event: H3Event) => {
33
+ const provider = event.context.params?.provider
34
+ if (!provider || !configs[provider]) {
35
+ throw createError({ status: 400, message: "Unknown OAuth provider" })
36
+ }
37
+
38
+ try {
39
+ const oauthHandler = { google: defineOAuthGoogleEventHandler, github: defineOAuthGitHubEventHandler }[provider]
40
+ if (!oauthHandler) {
41
+ throw createError({ status: 400, message: `OAuth handler not found for provider: ${provider}` })
42
+ }
43
+
44
+ return await oauthHandler({ config: configs[provider], async onSuccess(event: H3Event<EventHandlerRequest>, { user }: any) {
45
+ if (!user || typeof user !== "object") {
46
+ throw createError({ status: 400, message: `Invalid user data from ${provider}` })
47
+ }
48
+
49
+ const extractor = extractUserData[provider]
50
+ if (!extractor) {
51
+ throw createError({ status: 400, message: `User data extractor not found for provider: ${provider}` })
52
+ }
53
+
54
+ const userData = extractor(user)
55
+ if (!userData.id || !userData.email) {
56
+ throw createError({ status: 400, message: `Missing required user data from ${provider}` })
57
+ }
58
+
59
+ return handleOAuthUser(event, { ...userData, provider })
60
+ }, async onError(event: H3Event<EventHandlerRequest>) {
61
+ return sendRedirect(event, `/sign-in?error=${provider}_oauth_failed`)
62
+ } })(event)
63
+ }
64
+ catch (err: any) {
65
+ throw createError({ status: 500, message: "OAuth processing failed", data: { provider, error: err instanceof Error ? err.message : String(err) } })
66
+ }
67
+ })
@@ -0,0 +1,8 @@
1
+ export default defineEventHandler(async (event) => {
2
+ const user = await getUserFromSession(event)
3
+
4
+ await db.user.delete({ where: { id: user.id } })
5
+ await clearUserSession(event)
6
+
7
+ return { success: true, message: "User deleted successfully" }
8
+ })
@@ -0,0 +1,10 @@
1
+ export default defineEventHandler(async (event) => {
2
+ const user = await getUserFromSession(event)
3
+ if (!user) {
4
+ throw createError({ status: 404, message: "User not found" })
5
+ }
6
+
7
+ const userData = await db.user.findUnique({ where: { id: user.id } })
8
+
9
+ return { userData }
10
+ })
@@ -0,0 +1,48 @@
1
+ import type { H3Event } from "h3"
2
+
3
+ export async function handleOAuthUser(event: H3Event, userData: OAuthUserData) {
4
+ const { id: providerAccountId, name, email, image, provider } = userData
5
+
6
+ // Find existing account by provider
7
+ let account = await db.account.findUnique({
8
+ where: { provider_providerAccountId: { provider, providerAccountId } },
9
+ include: { user: true },
10
+ })
11
+
12
+ let user: any = account?.user ?? null
13
+ if (!user) {
14
+ user = await db.user.findUnique({ where: { email } })
15
+ }
16
+
17
+ // If still no user, create one
18
+ if (!user) {
19
+ user = await db.user.create({
20
+ data: {
21
+ email,
22
+ name: name?.trim() || email.split("@")[0],
23
+ image: image || undefined,
24
+ },
25
+ })
26
+ }
27
+
28
+ // Upsert account
29
+ account = await db.account.upsert({
30
+ where: { provider_providerAccountId: { provider, providerAccountId } },
31
+ update: {},
32
+ create: { userId: user.id, provider, providerAccountId },
33
+ include: { user: true },
34
+ })
35
+
36
+ user = account.user
37
+
38
+ // Build session object
39
+ const sessionUser = {
40
+ id: user.id,
41
+ email: user.email,
42
+ name: user.name,
43
+ image: user.image ?? null,
44
+ }
45
+
46
+ await setUserSession(event, { user: sessionUser, loggedInAt: new Date() })
47
+ return sendRedirect(event, "/admin/profile")
48
+ }
@@ -0,0 +1,15 @@
1
+ import { PrismaPg } from "@prisma/adapter-pg"
2
+ import dotenv from "dotenv"
3
+ import { PrismaClient } from "../../.data/prisma/client"
4
+
5
+ dotenv.config()
6
+
7
+ const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
8
+ const db = new PrismaClient({ adapter })
9
+
10
+ db.$connect().catch((err) => {
11
+ console.error("Failed to connect to database:", err)
12
+ process.exit(1)
13
+ })
14
+
15
+ export default db
@@ -0,0 +1,14 @@
1
+ import type { EventHandlerRequest, H3Event } from "h3"
2
+
3
+ /**
4
+ * Retrieves the authenticated user from the current session.
5
+ * Throws 401 if no valid session exists.
6
+ */
7
+ export async function getUserFromSession(event: H3Event<EventHandlerRequest>) {
8
+ const session = await getUserSession(event)
9
+ if (!session?.user?.id) {
10
+ throw createError({ status: 401, statusText: "Unauthorized" })
11
+ }
12
+
13
+ return session.user
14
+ }
@@ -0,0 +1,31 @@
1
+ declare module "#auth-utils" {
2
+ interface User {
3
+ id: string
4
+ email: string
5
+ name: string
6
+ image?: string | null
7
+ }
8
+
9
+ interface UserSession {
10
+ user: User
11
+ loggedInAt: Date
12
+ }
13
+
14
+ interface Account {
15
+ id: string
16
+ userId: string
17
+ provider: string
18
+ providerAccountId: string
19
+ createdAt?: Date | string
20
+ updatedAt?: Date | string
21
+ user?: User
22
+ }
23
+ }
24
+
25
+ interface OAuthUserData {
26
+ id: string
27
+ email: string
28
+ name: string
29
+ image: string | null
30
+ provider: "google" | "github"
31
+ }
@@ -0,0 +1,15 @@
1
+ interface User {
2
+ id: string
3
+ name: string
4
+ email: string
5
+ image?: string | null
6
+ createdAt?: Date | string
7
+ updatedAt?: Date | string
8
+ }
9
+
10
+ interface Toast {
11
+ id: string
12
+ message: string
13
+ type: "success" | "error"
14
+ duration?: number
15
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "references": [
3
+ {
4
+ "path": "./.nuxt/tsconfig.app.json"
5
+ },
6
+ {
7
+ "path": "./.nuxt/tsconfig.server.json"
8
+ },
9
+ {
10
+ "path": "./.nuxt/tsconfig.shared.json"
11
+ },
12
+ {
13
+ "path": "./.nuxt/tsconfig.node.json"
14
+ }
15
+ ],
16
+ // https://nuxt.com/docs/guide/concepts/typescript
17
+ "files": []
18
+ }
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <NuxtLayout>
3
+ <NuxtPage />
4
+ </NuxtLayout>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+
9
+ const { locale } = useI18n()
10
+ const isLoading = ref(true)
11
+
12
+ onMounted(() => {
13
+ const onLoad = () => (isLoading.value = false)
14
+ if (document.readyState === "complete") {
15
+ isLoading.value = false
16
+ }
17
+ else {
18
+ window.addEventListener("load", onLoad)
19
+ onBeforeUnmount(() => window.removeEventListener("load", onLoad))
20
+ }
21
+ })
22
+
23
+ useLocaleHead({ dir: true, seo: true })
24
+ useHead({
25
+ htmlAttrs: { lang: computed(() => locale.value) },
26
+ link: [{ rel: "icon", href: "/favicon.svg" }],
27
+ meta: [{ name: "viewport", content: "width=device-width, initial-scale=1" },],
28
+ })
29
+ </script>
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <nav class="flex w-full flex-row items-center justify-between gap-2 p-4">
3
+ <div class="flex flex-row items-center gap-2">
4
+ <nuxt-link to="/">
5
+ <icon name="simple-icons:nuxt" size="35" class="text-primary" />
6
+ </nuxt-link>
7
+
8
+ <div v-if="userStore.user" class="flex flex-row items-center gap-2">
9
+ <p class="text-sm">
10
+ {{ t('navbar.greeting', { name: userStore.user?.name }) }}
11
+ </p>
12
+ <button class="btn" @click="signOut">
13
+ {{ t('navbar.logout') }}
14
+ </button>
15
+ </div>
16
+
17
+ <div v-else class="flex flex-row items-center gap-2">
18
+ <p class="text-sm">
19
+ {{ t('navbar.unauthenticated') }}
20
+ </p>
21
+ <nuxt-link to="/sign-in" class="btn">
22
+ {{ t('navbar.signIn') }}
23
+ </nuxt-link>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="flex flex-row items-center gap-2">
28
+ <nuxt-link to="https://github.com/matimortari/nuxt-bake" class="btn">
29
+ <icon name="simple-icons:github" size="20" />
30
+ </nuxt-link>
31
+
32
+ <button class="btn" @click="toggleTheme">
33
+ <icon :name="themeIcon" size="20" />
34
+ </button>
35
+
36
+ <button v-for="language in availableLocales" :key="language" class="cursor-pointer outline-none hover:underline" @click="() => setLanguage(language)">
37
+ {{ t(`locale.${language}`) }}
38
+ </button>
39
+ </div>
40
+ </nav>
41
+ </template>
42
+
43
+ <script setup lang="ts">
44
+ const { t, locale, availableLocales } = useI18n()
45
+ const { clear } = useUserSession()
46
+ const { toggleTheme, themeIcon } = useTheme()
47
+ const userStore = useUserStore()
48
+
49
+ async function setLanguage(language: string) {
50
+ locale.value = language as "en-US" | "fr-FR"
51
+ localStorage.setItem("nuxt-lang", language)
52
+ await nextTick()
53
+ }
54
+
55
+ onMounted(async () => {
56
+ try {
57
+ await userStore.getUser()
58
+ }
59
+ catch (err: any) {
60
+ console.error("Failed to fetch user:", err)
61
+ }
62
+
63
+ const savedLang = localStorage.getItem("nuxt-lang")
64
+ if (savedLang && (savedLang === "en-US" || savedLang === "fr-FR")) {
65
+ locale.value = savedLang as "en-US" | "fr-FR"
66
+ await nextTick()
67
+ }
68
+ })
69
+
70
+ async function signOut() {
71
+ await clear()
72
+ return navigateTo("/")
73
+ }
74
+ </script>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="flex h-screen flex-col items-center justify-center gap-4">
3
+ <h1>
4
+ {{ error.status }}
5
+ </h1>
6
+
7
+ <p class="text-muted-foreground text-center">
8
+ {{ error.statusText || t("error.unknown") }}
9
+ </p>
10
+
11
+ <nuxt-link to="/">
12
+ {{ t("error.goHome") }}
13
+ </nuxt-link>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import type { NuxtError } from "#app"
19
+
20
+ defineProps<{
21
+ error: NuxtError
22
+ }>()
23
+
24
+ const { t } = useI18n()
25
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <div class="flex flex-col items-center justify-center gap-8">
3
+ <header class="flex w-full flex-col items-center gap-4 p-4 text-center">
4
+ <div class="flex flex-col items-center gap-2 text-center">
5
+ <icon name="simple-icons:nuxt" size="50" class="text-primary" />
6
+ <h1>
7
+ {{ t("index.header.title") }}
8
+ </h1>
9
+ <p>
10
+ {{ t("index.header.description") }}
11
+ </p>
12
+ </div>
13
+ </header>
14
+
15
+ <hr class="border-primary! w-4/5">
16
+
17
+ <div class="flex flex-col items-center gap-2">
18
+ <h2 class="text-lg font-semibold">
19
+ {{ t("index.features") }}
20
+ </h2>
21
+
22
+ <ul class="grid grid-cols-2 gap-4 md:grid-cols-4">
23
+ <li v-for="(feature, index) in features" :key="index" class="flex items-center gap-2 text-xs font-semibold md:text-sm">
24
+ <icon :name="feature.icon" size="20" class="text-primary" />
25
+ <span>{{ t(`index.featureList.${feature.key}`) }}</span>
26
+ </li>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ const { t } = useI18n()
34
+
35
+ const features = [
36
+ { icon: "simple-icons:nuxtdotjs", key: "nuxt" },
37
+ { icon: "simple-icons:typescript", key: "typescript" },
38
+ { icon: "simple-icons:tailwindcss", key: "tailwind" },
39
+ { icon: "simple-icons:iconify", key: "icons" },
40
+ { icon: "ph:text-aa", key: "fonts" },
41
+ { icon: "icon-park-outline:pineapple", key: "pinia" },
42
+ { icon: "ph:translate", key: "i18n" },
43
+ { icon: "ph:lock-key-open", key: "oauth" },
44
+ { icon: "simple-icons:prisma", key: "prisma" },
45
+ { icon: "simple-icons:eslint", key: "eslint" },
46
+ { icon: "ph:magnifying-glass", key: "seo" },
47
+ ]
48
+
49
+ useHead({
50
+ title: t("index.meta.title"),
51
+ meta: [{ name: "description", content: t("index.meta.description") }],
52
+ })
53
+ </script>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <div class="flex flex-col items-center justify-center gap-8">
3
+ <header class="my-4 flex flex-col items-center justify-center gap-2">
4
+ <h2>{{ t("signIn.header") }}</h2>
5
+ </header>
6
+
7
+ <div class="my-4 flex flex-col items-center gap-4">
8
+ <p class="text-muted-foreground text-lg font-semibold">
9
+ {{ t("signIn.chooseProvider") }}
10
+ </p>
11
+ <div class="flex flex-row items-center gap-4">
12
+ <button
13
+ v-for="provider in providers" :key="provider.name"
14
+ class="btn" @click="navigateTo(`/api/auth/${provider.name}`, { external: true })"
15
+ >
16
+ <icon :name="provider.icon" size="25" />
17
+ <span>{{ t(`signIn.providers.${provider.name}`) }}</span>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ const { t } = useI18n()
26
+
27
+ const providers = [
28
+ { name: "github", icon: "simple-icons:github" },
29
+ { name: "google", icon: "simple-icons:google" },
30
+ ]
31
+
32
+ useSeoMeta({
33
+ title: t("signIn.meta.title"),
34
+ description: t("signIn.meta.description"),
35
+ ogTitle: t("signIn.meta.title"),
36
+ ogDescription: t("signIn.meta.description"),
37
+ robots: "noindex, nofollow",
38
+ })
39
+ </script>
@@ -0,0 +1,14 @@
1
+ import en from "~/utils/locales/en-US.json"
2
+ import br from "~/utils/locales/fr-FR.json"
3
+
4
+ export default defineI18nConfig(() => {
5
+ return {
6
+ legacy: false,
7
+ langDir: "./locales",
8
+ messages: { "en-US": en, "fr-FR": br },
9
+ locales: [
10
+ { code: "en-US", iso: "en-US" },
11
+ { code: "fr-FR", iso: "fr-FR" },
12
+ ],
13
+ }
14
+ })
@@ -0,0 +1,50 @@
1
+ {
2
+ "locale.en-US": "EN",
3
+ "locale.fr-FR": "FR",
4
+ "index": {
5
+ "meta": {
6
+ "title": "Nuxt Bake",
7
+ "description": "If you're seeing this, it means you've successfully set up your Nuxt.js project via nuxt-bake."
8
+ },
9
+ "header": {
10
+ "title": "Nuxt Bake",
11
+ "description": "If you're seeing this, it means you've successfully set up your Nuxt.js project via nuxt-bake."
12
+ },
13
+ "features": "Features",
14
+ "featureList": {
15
+ "nuxt": "Nuxt 4",
16
+ "typescript": "TypeScript",
17
+ "tailwind": "Tailwind CSS 4",
18
+ "icons": "Nuxt Icons",
19
+ "fonts": "Google Fonts",
20
+ "pinia": "Pinia",
21
+ "i18n": "Nuxt i18n",
22
+ "oauth": "OAuth",
23
+ "prisma": "Prisma",
24
+ "eslint": "ESLint",
25
+ "seo": "SEO"
26
+ }
27
+ },
28
+ "signIn": {
29
+ "meta": {
30
+ "title": "Sign In",
31
+ "description": "Sign In page"
32
+ },
33
+ "header": "Sign In",
34
+ "chooseProvider": "Choose a provider to continue.",
35
+ "providers": {
36
+ "github": "Sign In With GitHub",
37
+ "google": "Sign In With Google"
38
+ }
39
+ },
40
+ "error": {
41
+ "unknown": "Unknown error",
42
+ "goHome": "Go back home"
43
+ },
44
+ "navbar": {
45
+ "greeting": "Hi",
46
+ "unauthenticated": "Unauthenticated",
47
+ "logout": "Logout",
48
+ "signIn": "Sign In"
49
+ }
50
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "locale.en-US": "EN",
3
+ "locale.fr-FR": "FR",
4
+ "index": {
5
+ "meta": {
6
+ "title": "Nuxt.js Départ",
7
+ "description": "Si vous voyez ceci, cela signifie que vous avez configuré avec succès votre projet Nuxt.js via nuxt-bake."
8
+ },
9
+ "header": {
10
+ "title": "Nuxt.js Départ",
11
+ "description": "Si vous voyez ceci, cela signifie que vous avez configuré avec succès votre projet Nuxt.js via nuxt-bake."
12
+ },
13
+ "features": "Fonctionnalités",
14
+ "featureList": {
15
+ "nuxt": "Nuxt 4",
16
+ "typescript": "TypeScript",
17
+ "tailwind": "Tailwind CSS 4",
18
+ "icons": "Nuxt Icons",
19
+ "fonts": "Google Fonts",
20
+ "pinia": "Pinia",
21
+ "i18n": "Nuxt i18n",
22
+ "oauth": "OAuth",
23
+ "prisma": "Prisma",
24
+ "eslint": "ESLint",
25
+ "seo": "SEO"
26
+ }
27
+ },
28
+ "signIn": {
29
+ "meta": {
30
+ "title": "Connexion",
31
+ "description": "Page de connexion"
32
+ },
33
+ "header": "Connexion",
34
+ "chooseProvider": "Choisissez un fournisseur pour continuer.",
35
+ "providers": {
36
+ "github": "Se connecter avec GitHub",
37
+ "google": "Se connecter avec Google"
38
+ }
39
+ },
40
+ "error": {
41
+ "unknown": "Erreur inconnue",
42
+ "goHome": "Retour à l'accueil"
43
+ },
44
+ "navbar": {
45
+ "greeting": "Salut",
46
+ "unauthenticated": "Non authentifié",
47
+ "logout": "Déconnexion",
48
+ "signIn": "Connexion"
49
+ }
50
+ }
@@ -0,0 +1,51 @@
1
+ import tailwindcss from "@tailwindcss/vite"
2
+
3
+ export default defineNuxtConfig({
4
+ modules: [
5
+ "@nuxt/fonts",
6
+ "@nuxt/icon",
7
+ "@nuxtjs/color-mode",
8
+ "@nuxtjs/i18n",
9
+ "@nuxtjs/seo",
10
+ "@pinia/nuxt",
11
+ "nuxt-auth-utils",
12
+ ],
13
+ runtimeConfig: {
14
+ public: {
15
+ baseUrl: process.env.NUXT_PUBLIC_BASE_URL,
16
+ },
17
+ },
18
+ vite: {
19
+ plugins: [tailwindcss() as any],
20
+ },
21
+ css: ["~/assets/styles.css"],
22
+ site: {
23
+ url: process.env.NUXT_PUBLIC_BASE_URL,
24
+ },
25
+ colorMode: {
26
+ classSuffix: "",
27
+ preference: "system",
28
+ fallback: "light",
29
+ storageKey: "nuxt-color-mode",
30
+ },
31
+ i18n: {
32
+ restructureDir: "app/utils",
33
+ baseUrl: process.env.NUXT_PUBLIC_BASE_URL,
34
+ locales: [
35
+ { code: "en-US", iso: "en-US", file: "en-US.json" },
36
+ { code: "fr-FR", iso: "fr-FR", file: "fr-FR.json" },
37
+ ],
38
+ defaultLocale: "en-US",
39
+ strategy: "no_prefix",
40
+ detectBrowserLanguage: {
41
+ useCookie: true,
42
+ cookieKey: "nuxt-lang",
43
+ alwaysRedirect: true,
44
+ fallbackLocale: "en-US",
45
+ },
46
+ },
47
+ icon: {
48
+ mode: "svg",
49
+ clientBundle: { scan: true },
50
+ },
51
+ })
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <div class="flex flex-col items-center justify-center gap-8">
3
+ <header class="flex w-full flex-col items-center gap-4 p-4">
4
+ <div class="flex flex-col items-center gap-2 text-center">
5
+ <icon name="simple-icons:nuxt" size="50" class="text-primary" />
6
+ <h1>
7
+ Nuxt Bake
8
+ </h1>
9
+ <p>
10
+ If you're seeing this, it means you've successfully set up your Nuxt.js project via <code class="text-primary">Nuxt Bake</code>.
11
+ </p>
12
+ </div>
13
+ </header>
14
+
15
+ <hr class="border-primary! w-4/5">
16
+
17
+ <div class="flex flex-col items-center gap-2">
18
+ <h2 class="text-lg font-semibold">
19
+ Features
20
+ </h2>
21
+
22
+ <ul class="grid grid-cols-2 gap-4 md:grid-cols-6">
23
+ <li v-for="(feature, index) in features" :key="index" class="flex flex-row items-center gap-2 text-xs font-semibold md:text-sm">
24
+ <icon :name="feature.icon" :size="20" class="text-primary" />
25
+ <span>{{ feature.label }}</span>
26
+ </li>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ const features = [
34
+ { icon: "simple-icons:nuxtdotjs", label: "Nuxt 4" },
35
+ { icon: "simple-icons:typescript", label: "TypeScript" },
36
+ { icon: "simple-icons:tailwindcss", label: "Tailwind CSS 4" },
37
+ { icon: "simple-icons:iconify", label: "Nuxt Icons" },
38
+ { icon: "ph:text-aa", label: "Google Fonts" },
39
+ { icon: "icon-park-outline:pineapple", label: "Pinia" },
40
+ { icon: "ph:lock-key-open", label: "OAuth" },
41
+ { icon: "simple-icons:prisma", label: "Prisma" },
42
+ { icon: "simple-icons:playwright", label: "Playwright" },
43
+ { icon: "simple-icons:vitest", label: "Vitest" },
44
+ { icon: "simple-icons:eslint", label: "ESLint" },
45
+ ]
46
+
47
+ useHead({
48
+ title: "Home",
49
+ meta: [{ name: "description", content: "Home page." }],
50
+ })
51
+ </script>