Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 49 additions & 37 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
name: Next.js CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run Lint
run: npm run lint

- name: Build project
run: npm run build
name: Next.js CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [22.x]

steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ matrix.node-version }}
cache: ${{ hashFiles('package-lock.json') != '' && 'npm' || '' }}
cache-dependency-path: ${{ hashFiles('package-lock.json') != '' && 'package-lock.json' || '' }}

- name: Install dependencies
run: |
if [ -f package-lock.json ]; then
npm ci
else
npm install
fi

- name: Run Lint
run: npm run lint

- name: Build project
run: npm run build
66 changes: 34 additions & 32 deletions .github/workflows/nextjs.yml
Original file line number Diff line number Diff line change
@@ -1,87 +1,89 @@
# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started
#
name: Deploy Next.js site to Pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
pull_request:
branches: ["main"]
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
group: ${{ github.event_name == 'pull_request' && format('pages-pr-{0}', github.event.pull_request.number) || 'pages' }}
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/yarn.lock" ]; then
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT
echo "lockfile=yarn.lock" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/package.json" ]; then
elif [ -f "${{ github.workspace }}/package-lock.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=ci" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
echo "lockfile=package-lock.json" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/package.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
echo "lockfile=" >> $GITHUB_OUTPUT
exit 0
else
echo "Unable to determine package manager"
exit 1
fi

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
cache: ${{ steps.detect-package-manager.outputs.manager }}
node-version: "22"
cache: ${{ steps.detect-package-manager.outputs.lockfile != '' && steps.detect-package-manager.outputs.manager || '' }}
cache-dependency-path: ${{ steps.detect-package-manager.outputs.lockfile }}

- name: Setup Pages
uses: actions/configure-pages@v5
with:
# Automatically inject basePath in your Next.js configuration file and disable
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
#
# You may remove this line if you want to manage the configuration yourself.
static_site_generator: next
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v5

- name: Restore cache
uses: actions/cache@v4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-

- name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}

- name: Build with Next.js
run: ${{ steps.detect-package-manager.outputs.runner }} next build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: ./out

# Deployment job
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
Expand All @@ -90,4 +92,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ TODO: Project-specific context and directives for AI agents.

## 📦 Project Boundaries

- **Config alias map:** Webpack & Turbopack alias resolvers are manually defined in [`next.config.ts`](next.config.ts) mapping `"next-intl/config"` to our local request setup. Do not import the native `@swc/core` compiler plugin wrapper `createNextIntlPlugin` directly to avoid security policy native module blocks.
- **Config Alias Map:** Turbopack and Webpack alias resolvers map `"next-intl/config"` to our local request setup inside [`next.config.ts`](next.config.ts) using `createNextIntlPlugin("./src/i18n/request.ts")`.
- **Branding Assets:** The official branding logo, favicon, and style specifications reside inside [`public/brand/`](public/brand/).

11 changes: 6 additions & 5 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
import path from "path";

const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");

const nextConfig: NextConfig = {
output: "export",
images: {
unoptimized: true,
},
turbopack: {
root: path.resolve(process.cwd()),
resolveAlias: {
"next-intl/config": "./src/i18n/request.ts",
},
root: path.resolve(__dirname),
},
};

export default nextConfig;
export default withNextIntl(nextConfig);

16 changes: 11 additions & 5 deletions src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import { Inter, Noto_Sans_Devanagari } from "next/font/google";
import { routing } from "@/i18n/routing";
import { generateLocaleMetadata } from "@/i18n/metadata";
import { notFound } from "next/navigation";
import { getMessages, setRequestLocale } from "next-intl/server";
import { setRequestLocale } from "next-intl/server";
import { NextIntlClientProvider } from "next-intl";
import { messagesMap, defaultMessages } from "@/i18n/messages";
import { ThemeProvider } from "@/components/providers/theme-provider";
import { LenisProvider } from "@/components/providers/lenis-provider";
import "./globals.css";

const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
display: "swap",
});

const devanagari = Noto_Sans_Devanagari({
variable: "--font-devanagari",
subsets: ["devanagari"],
display: "swap",
});

export const dynamicParams = false;
Expand All @@ -34,6 +37,7 @@ export async function generateMetadata({
if (!routing.locales.includes(locale as (typeof routing.locales)[number])) {
notFound();
}
setRequestLocale(locale);
return generateLocaleMetadata(locale, "Home");
}

Expand All @@ -55,17 +59,17 @@ export default async function RootLayout({
// Enable static rendering
setRequestLocale(locale);

// Provide messages to Client Components
const messages = await getMessages({ locale });
// Provide messages to Client Components directly via messagesMap
const messages = messagesMap[locale] || defaultMessages;

return (
<html
lang={locale}
className={`${inter.variable} ${devanagari.variable} h-full antialiased`}
suppressHydrationWarning
>
<body className="min-h-full flex flex-col" suppressHydrationWarning>
<NextIntlClientProvider messages={messages}>
<body className="min-h-full flex flex-col bg-background text-foreground" suppressHydrationWarning>
<NextIntlClientProvider locale={locale} messages={messages} timeZone="UTC">
<ThemeProvider>
<LenisProvider>
{children}
Expand All @@ -76,3 +80,5 @@ export default async function RootLayout({
</html>
);
}


11 changes: 11 additions & 0 deletions src/i18n/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import enMessages from '../messages/en.json';
import hiMessages from '../messages/hi.json';

export type Messages = typeof enMessages;

export const messagesMap: Record<string, Record<string, unknown>> = {
en: enMessages as Record<string, unknown>,
hi: hiMessages as Record<string, unknown>,
};

export const defaultMessages = enMessages;
22 changes: 11 additions & 11 deletions src/i18n/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';
import { messagesMap, defaultMessages, Messages } from './messages';

export async function generateLocaleMetadata(
locale: string,
namespace: string
namespace: 'Home' = 'Home'
): Promise<Metadata> {
const t = await getTranslations({ locale, namespace });
const messages = (messagesMap[locale] || defaultMessages) as Messages;
const meta = messages[namespace] || defaultMessages.Home;

const rawSiteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://TODO:project.aossie.org';
const siteUrl = rawSiteUrl.replace(/\/$/, '');
const localeUrl = `${siteUrl}/${locale}`;


return {
title: t('metaTitle'),
description: t('metaDescription'),
title: meta.metaTitle,
description: meta.metaDescription,
icons: {
icon: '/brand/icons/favicon.ico',
},
Expand All @@ -26,10 +26,9 @@ export async function generateLocaleMetadata(
},
},
openGraph: {
title: t('metaTitle'),
description: t('metaDescription'),
title: meta.metaTitle,
description: meta.metaDescription,
url: localeUrl,

siteName: 'AOSSIE',
images: [
{
Expand All @@ -44,9 +43,10 @@ export async function generateLocaleMetadata(
},
twitter: {
card: 'summary_large_image',
title: t('metaTitle'),
description: t('metaDescription'),
title: meta.metaTitle,
description: meta.metaDescription,
images: [`${siteUrl}/brand/icons/aossie_logo.svg`],
},
};
}

5 changes: 3 additions & 2 deletions src/i18n/request.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { getRequestConfig } from 'next-intl/server';
import { messagesMap, defaultMessages } from './messages';
import { routing } from './routing';

export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;

// Ensure the locale is valid
if (!locale || !routing.locales.includes(locale as (typeof routing.locales)[number])) {
locale = routing.defaultLocale;
}

return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
messages: messagesMap[locale] || defaultMessages,
};
});

Loading