add astro template for front #13

Merged
Clement merged 18 commits from feat/astro-template-for-front into master 2024-05-21 08:07:58 +00:00
8 changed files with 476 additions and 465 deletions
Showing only changes of commit 7f6eac4102 - Show all commits

View File

@ -12,8 +12,8 @@ metadata:
canonical: https://astrowind.vercel.app/astrowind-template-in-depth canonical: https://astrowind.vercel.app/astrowind-template-in-depth
--- ---
import DListItem from '~/components/ui/DListItem.astro'; import DListItem from 'components/ui/DListItem.astro';
import ToggleTheme from '~/components/common/ToggleTheme.astro'; import ToggleTheme from 'components/common/ToggleTheme.astro';
## Overview ## Overview

View File

@ -8,7 +8,7 @@ tags:
- Astro - Astro
--- ---
import Logo from '~/components/Logo.astro'; import Logo from 'components/Logo.astro';
import { YouTube, Tweet, Vimeo } from 'astro-embed'; import { YouTube, Tweet, Vimeo } from 'astro-embed';
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

View File

@ -1,9 +1,14 @@
--- ---
import Layout from 'layouts/Layout.astro'; import Layout from 'layouts/PageLayout.astro';
import 'leaflet/dist/leaflet.css' import 'leaflet/dist/leaflet.css'
const metadata = {
title: 'Maps',
ignoreTitleTemplate: true,
};
--- ---
<Layout title="maps test"> <Layout metadata={metadata}>
<div class="w-52 h-52" id="map" /> <div class="w-52 h-52" id="map" />

View File

@ -1,8 +1,8 @@
import { getRssString } from '@astrojs/rss'; import { getRssString } from '@astrojs/rss';
import { SITE, METADATA, APP_BLOG } from 'astrowind:config'; import { SITE, METADATA, APP_BLOG } from 'astrowind:config';
import { fetchPosts } from '~/utils/blog'; import { fetchPosts } from 'utils/blog';
import { getPermalink } from '~/utils/permalinks'; import { getPermalink } from 'utils/permalinks';
export const GET = async () => { export const GET = async () => {
if (!APP_BLOG.isEnabled) { if (!APP_BLOG.isEnabled) {

View File

@ -1,9 +1,11 @@
import type { PaginateFunction } from 'astro'; /* eslint-disable @typescript-eslint/no-unused-expressions */
import { getCollection } from 'astro:content'; /* eslint-disable id-length */
import type { CollectionEntry } from 'astro:content'; import type { PaginateFunction } from 'astro'
import type { Post } from '~/types'; import { getCollection } from 'astro:content'
import { APP_BLOG } from 'astrowind:config'; import type { CollectionEntry } from 'astro:content'
import { cleanSlug, trimSlash, BLOG_BASE, POST_PERMALINK_PATTERN, CATEGORY_BASE, TAG_BASE } from './permalinks'; import type { Post } from 'types'
import { APP_BLOG } from 'astrowind:config'
import { cleanSlug, trimSlash, BLOG_BASE, POST_PERMALINK_PATTERN, CATEGORY_BASE, TAG_BASE } from './permalinks'
const generatePermalink = async ({ const generatePermalink = async ({
id, id,
@ -11,17 +13,17 @@ const generatePermalink = async ({
publishDate, publishDate,
category, category,
}: { }: {
id: string; id: string
slug: string; slug: string
publishDate: Date; publishDate: Date
category: string | undefined; category: string | undefined
}) => { }) => {
const year = String(publishDate.getFullYear()).padStart(4, '0'); const year = String(publishDate.getFullYear()).padStart(4, '0')
const month = String(publishDate.getMonth() + 1).padStart(2, '0'); const month = String(publishDate.getMonth() + 1).padStart(2, '0')
const day = String(publishDate.getDate()).padStart(2, '0'); const day = String(publishDate.getDate()).padStart(2, '0')
const hour = String(publishDate.getHours()).padStart(2, '0'); const hour = String(publishDate.getHours()).padStart(2, '0')
const minute = String(publishDate.getMinutes()).padStart(2, '0'); const minute = String(publishDate.getMinutes()).padStart(2, '0')
const second = String(publishDate.getSeconds()).padStart(2, '0'); const second = String(publishDate.getSeconds()).padStart(2, '0')
const permalink = POST_PERMALINK_PATTERN.replace('%slug%', slug) const permalink = POST_PERMALINK_PATTERN.replace('%slug%', slug)
.replace('%id%', id) .replace('%id%', id)
@ -31,18 +33,18 @@ const generatePermalink = async ({
.replace('%day%', day) .replace('%day%', day)
.replace('%hour%', hour) .replace('%hour%', hour)
.replace('%minute%', minute) .replace('%minute%', minute)
.replace('%second%', second); .replace('%second%', second)
return permalink return permalink
.split('/') .split('/')
.map((el) => trimSlash(el)) .map((el) => trimSlash(el))
.filter((el) => !!el) .filter((el) => !!el)
.join('/'); .join('/')
}; }
const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> => { const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> => {
const { id, slug: rawSlug = '', data } = post; const { id, slug: rawSlug = '', data } = post
const { Content, remarkPluginFrontmatter } = await post.render(); const { Content, remarkPluginFrontmatter } = await post.render()
const { const {
publishDate: rawPublishDate = new Date(), publishDate: rawPublishDate = new Date(),
@ -55,23 +57,23 @@ const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> =
author, author,
draft = false, draft = false,
metadata = {}, metadata = {},
} = data; } = data
const slug = cleanSlug(rawSlug); // cleanSlug(rawSlug.split('/').pop()); const slug = cleanSlug(rawSlug) // cleanSlug(rawSlug.split('/').pop())
const publishDate = new Date(rawPublishDate); const publishDate = new Date(rawPublishDate)
const updateDate = rawUpdateDate ? new Date(rawUpdateDate) : undefined; const updateDate = rawUpdateDate ? new Date(rawUpdateDate) : undefined
const category = rawCategory const category = rawCategory
? { ? {
slug: cleanSlug(rawCategory), slug: cleanSlug(rawCategory),
title: rawCategory, title: rawCategory,
} }
: undefined; : undefined
const tags = rawTags.map((tag: string) => ({ const tags = rawTags.map((tag: string) => ({
slug: cleanSlug(tag), slug: cleanSlug(tag),
title: tag, title: tag,
})); }))
return { return {
id: id, id: id,
@ -97,111 +99,112 @@ const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> =
// or 'content' in case you consume from API // or 'content' in case you consume from API
readingTime: remarkPluginFrontmatter?.readingTime, readingTime: remarkPluginFrontmatter?.readingTime,
}; }
}; }
const load = async function(): Promise<Array<Post>> { const load = async function(): Promise<Array<Post>> {
const posts = await getCollection('post'); const posts = await getCollection('post')
const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post)); const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post))
const results = (await Promise.all(normalizedPosts)) const results = (await Promise.all(normalizedPosts))
.sort((a, b) => b.publishDate.valueOf() - a.publishDate.valueOf()) .sort((a, b) => b.publishDate.valueOf() - a.publishDate.valueOf())
.filter((post) => !post.draft); .filter((post) => !post.draft)
return results; return results
}; }
let _posts: Array<Post>; let _posts: Array<Post>
/** */ /** */
export const isBlogEnabled = APP_BLOG.isEnabled; export const isBlogEnabled = APP_BLOG.isEnabled
export const isRelatedPostsEnabled = APP_BLOG.isRelatedPostsEnabled; export const isRelatedPostsEnabled = APP_BLOG.isRelatedPostsEnabled
export const isBlogListRouteEnabled = APP_BLOG.list.isEnabled; export const isBlogListRouteEnabled = APP_BLOG.list.isEnabled
export const isBlogPostRouteEnabled = APP_BLOG.post.isEnabled; export const isBlogPostRouteEnabled = APP_BLOG.post.isEnabled
export const isBlogCategoryRouteEnabled = APP_BLOG.category.isEnabled; export const isBlogCategoryRouteEnabled = APP_BLOG.category.isEnabled
export const isBlogTagRouteEnabled = APP_BLOG.tag.isEnabled; export const isBlogTagRouteEnabled = APP_BLOG.tag.isEnabled
export const blogListRobots = APP_BLOG.list.robots; export const blogListRobots = APP_BLOG.list.robots
export const blogPostRobots = APP_BLOG.post.robots; export const blogPostRobots = APP_BLOG.post.robots
export const blogCategoryRobots = APP_BLOG.category.robots; export const blogCategoryRobots = APP_BLOG.category.robots
export const blogTagRobots = APP_BLOG.tag.robots; export const blogTagRobots = APP_BLOG.tag.robots
export const blogPostsPerPage = APP_BLOG?.postsPerPage; export const blogPostsPerPage = APP_BLOG?.postsPerPage
/** */ /** */
export const fetchPosts = async (): Promise<Array<Post>> => { export const fetchPosts = async (): Promise<Array<Post>> => {
if (!_posts) { if (!_posts) {
_posts = await load(); // eslint-disable-next-line require-atomic-updates
_posts = await load()
} }
return _posts; return _posts
}; }
/** */ /** */
export const findPostsBySlugs = async (slugs: Array<string>): Promise<Array<Post>> => { export const findPostsBySlugs = async (slugs: Array<string>): Promise<Array<Post>> => {
if (!Array.isArray(slugs)) return []; if (!Array.isArray(slugs)) {return []}
const posts = await fetchPosts(); const posts = await fetchPosts()
return slugs.reduce(function(r: Array<Post>, slug: string) { return slugs.reduce(function(r: Array<Post>, slug: string) {
posts.some(function(post: Post) { posts.some(function(post: Post) {
return slug === post.slug && r.push(post); return slug === post.slug && r.push(post)
}); })
return r; return r
}, []); }, [])
}; }
/** */ /** */
export const findPostsByIds = async (ids: Array<string>): Promise<Array<Post>> => { export const findPostsByIds = async (ids: Array<string>): Promise<Array<Post>> => {
if (!Array.isArray(ids)) return []; if (!Array.isArray(ids)) {return []}
const posts = await fetchPosts(); const posts = await fetchPosts()
return ids.reduce(function(r: Array<Post>, id: string) { return ids.reduce(function(r: Array<Post>, id: string) {
posts.some(function(post: Post) { posts.some(function(post: Post) {
return id === post.id && r.push(post); return id === post.id && r.push(post)
}); })
return r; return r
}, []); }, [])
}; }
/** */ /** */
export const findLatestPosts = async ({ count }: { count?: number }): Promise<Array<Post>> => { export const findLatestPosts = async ({ count }: { count?: number }): Promise<Array<Post>> => {
const _count = count || 4; const _count = count || 4
const posts = await fetchPosts(); const posts = await fetchPosts()
return posts ? posts.slice(0, _count) : []; return posts ? posts.slice(0, _count) : []
}; }
/** */ /** */
export const getStaticPathsBlogList = async ({ paginate }: { paginate: PaginateFunction }) => { export const getStaticPathsBlogList = async ({ paginate }: { paginate: PaginateFunction }) => {
if (!isBlogEnabled || !isBlogListRouteEnabled) return []; if (!isBlogEnabled || !isBlogListRouteEnabled) {return []}
return paginate(await fetchPosts(), { return paginate(await fetchPosts(), {
params: { blog: BLOG_BASE || undefined }, params: { blog: BLOG_BASE || undefined },
pageSize: blogPostsPerPage, pageSize: blogPostsPerPage,
}); })
}; }
/** */ /** */
export const getStaticPathsBlogPost = async () => { export const getStaticPathsBlogPost = async () => {
if (!isBlogEnabled || !isBlogPostRouteEnabled) return []; if (!isBlogEnabled || !isBlogPostRouteEnabled) {return []}
return (await fetchPosts()).flatMap((post) => ({ return (await fetchPosts()).flatMap((post) => ({
params: { params: {
blog: post.permalink, blog: post.permalink,
}, },
props: { post }, props: { post },
})); }))
}; }
/** */ /** */
export const getStaticPathsBlogCategory = async ({ paginate }: { paginate: PaginateFunction }) => { export const getStaticPathsBlogCategory = async ({ paginate }: { paginate: PaginateFunction }) => {
if (!isBlogEnabled || !isBlogCategoryRouteEnabled) return []; if (!isBlogEnabled || !isBlogCategoryRouteEnabled) {return []}
const posts = await fetchPosts(); const posts = await fetchPosts()
const categories = {}; const categories = {}
posts.map((post) => { posts.map((post) => {
post.category?.slug && (categories[post.category?.slug] = post.category); post.category?.slug && (categories[post.category?.slug] = post.category)
}); })
return Array.from(Object.keys(categories)).flatMap((categorySlug) => return Array.from(Object.keys(categories)).flatMap((categorySlug) =>
paginate( paginate(
@ -212,21 +215,21 @@ export const getStaticPathsBlogCategory = async ({ paginate }: { paginate: Pagin
props: { category: categories[categorySlug] }, props: { category: categories[categorySlug] },
} }
) )
); )
}; }
/** */ /** */
export const getStaticPathsBlogTag = async ({ paginate }: { paginate: PaginateFunction }) => { export const getStaticPathsBlogTag = async ({ paginate }: { paginate: PaginateFunction }) => {
if (!isBlogEnabled || !isBlogTagRouteEnabled) return []; if (!isBlogEnabled || !isBlogTagRouteEnabled) {return []}
const posts = await fetchPosts(); const posts = await fetchPosts()
const tags = {}; const tags = {}
posts.map((post) => { posts.map((post) => {
Array.isArray(post.tags) && Array.isArray(post.tags) &&
post.tags.map((tag) => { post.tags.map((tag) => {
tags[tag?.slug] = tag; tags[tag?.slug] = tag
}); })
}); })
return Array.from(Object.keys(tags)).flatMap((tagSlug) => return Array.from(Object.keys(tags)).flatMap((tagSlug) =>
paginate( paginate(
@ -237,42 +240,42 @@ export const getStaticPathsBlogTag = async ({ paginate }: { paginate: PaginateFu
props: { tag: tags[tagSlug] }, props: { tag: tags[tagSlug] },
} }
) )
); )
}; }
/** */ /** */
export async function getRelatedPosts(originalPost: Post, maxResults: number = 4): Promise<Post[]> { export async function getRelatedPosts(originalPost: Post, maxResults = 4): Promise<Array<Post>> {
const allPosts = await fetchPosts(); const allPosts = await fetchPosts()
const originalTagsSet = new Set(originalPost.tags ? originalPost.tags.map((tag) => tag.slug) : []); const originalTagsSet = new Set(originalPost.tags ? originalPost.tags.map((tag) => tag.slug) : [])
const postsWithScores = allPosts.reduce((acc: { post: Post; score: number }[], iteratedPost: Post) => { const postsWithScores = allPosts.reduce((acc: Array<{ post: Post, score: number }>, iteratedPost: Post) => {
if (iteratedPost.slug === originalPost.slug) return acc; if (iteratedPost.slug === originalPost.slug) {return acc}
let score = 0; let score = 0
if (iteratedPost.category && originalPost.category && iteratedPost.category.slug === originalPost.category.slug) { if (iteratedPost.category && originalPost.category && iteratedPost.category.slug === originalPost.category.slug) {
score += 5; score += 5
} }
if (iteratedPost.tags) { if (iteratedPost.tags) {
iteratedPost.tags.forEach((tag) => { iteratedPost.tags.forEach((tag) => {
if (originalTagsSet.has(tag.slug)) { if (originalTagsSet.has(tag.slug)) {
score += 1; score += 1
} }
}); })
} }
acc.push({ post: iteratedPost, score }); acc.push({ post: iteratedPost, score })
return acc; return acc
}, []); }, [])
postsWithScores.sort((a, b) => b.score - a.score); postsWithScores.sort((a, b) => b.score - a.score)
const selectedPosts: Post[] = []; const selectedPosts: Array<Post> = []
let i = 0; let i = 0
while (selectedPosts.length < maxResults && i < postsWithScores.length) { while (selectedPosts.length < maxResults && i < postsWithScores.length) {
selectedPosts.push(postsWithScores[i].post); selectedPosts.push(postsWithScores[i].post)
i++; i++
} }
return selectedPosts; return selectedPosts
} }

View File

@ -1,46 +1,47 @@
import slugify from 'limax'; /* eslint-disable max-depth */
/* eslint-disable complexity */
import slugify from 'limax'
import { SITE, APP_BLOG } from 'astrowind:config'; import { SITE, APP_BLOG } from 'astrowind:config'
import { trim } from '~/utils/utils'; import { trim } from 'utils/utils'
export const trimSlash = (s: string) => trim(trim(s, '/')); export const trimSlash = (s: string) => trim(trim(s, '/'))
const createPath = (...params: string[]) => { const createPath = (...params: Array<string>) => {
const paths = params const paths = params
.map((el) => trimSlash(el)) .map((el) => trimSlash(el))
.filter((el) => !!el) .filter((el) => !!el)
.join('/'); .join('/')
return '/' + paths + (SITE.trailingSlash && paths ? '/' : ''); return '/' + paths + (SITE.trailingSlash && paths ? '/' : '')
}; }
const BASE_PATHNAME = SITE.base || '/'; const BASE_PATHNAME = SITE.base || '/'
export const cleanSlug = (text = '') => export const cleanSlug = (text = '') =>
trimSlash(text) trimSlash(text)
.split('/') .split('/')
.map((slug) => slugify(slug)) .map((slug) => slugify(slug))
.join('/'); .join('/')
export const BLOG_BASE = cleanSlug(APP_BLOG?.list?.pathname); export const BLOG_BASE = cleanSlug(APP_BLOG?.list?.pathname)
export const CATEGORY_BASE = cleanSlug(APP_BLOG?.category?.pathname); export const CATEGORY_BASE = cleanSlug(APP_BLOG?.category?.pathname)
export const TAG_BASE = cleanSlug(APP_BLOG?.tag?.pathname) || 'tag'; export const TAG_BASE = cleanSlug(APP_BLOG?.tag?.pathname) || 'tag'
export const POST_PERMALINK_PATTERN = trimSlash(APP_BLOG?.post?.permalink || `${BLOG_BASE}/%slug%`); export const POST_PERMALINK_PATTERN = trimSlash(APP_BLOG?.post?.permalink || `${BLOG_BASE}/%slug%`)
/** */ /** */
export const getCanonical = (path = ''): string | URL => { export const getCanonical = (path = ''): string | URL => {
const url = String(new URL(path, SITE.site)); const url = String(new URL(path, SITE.site))
if (SITE.trailingSlash == false && path && url.endsWith('/')) { if (SITE.trailingSlash === false && path && url.endsWith('/')) {
return url.slice(0, -1); return url.slice(0, -1)
} else if (SITE.trailingSlash == true && path && !url.endsWith('/')) { } else if (SITE.trailingSlash == true && path && !url.endsWith('/')) {
return url + '/'; return url + '/'
}
return url
} }
return url;
};
/** */
export const getPermalink = (slug = '', type = 'page'): string => { export const getPermalink = (slug = '', type = 'page'): string => {
let permalink: string; let permalink: string
if ( if (
slug.startsWith('https://') || slug.startsWith('https://') ||
@ -49,48 +50,48 @@ export const getPermalink = (slug = '', type = 'page'): string => {
slug.startsWith('#') || slug.startsWith('#') ||
slug.startsWith('javascript:') slug.startsWith('javascript:')
) { ) {
return slug; return slug
} }
switch (type) { switch (type) {
case 'home': case 'home':
permalink = getHomePermalink(); permalink = getHomePermalink()
break; break
case 'blog': case 'blog':
permalink = getBlogPermalink(); permalink = getBlogPermalink()
break; break
case 'asset': case 'asset':
permalink = getAsset(slug); permalink = getAsset(slug)
break; break
case 'category': case 'category':
permalink = createPath(CATEGORY_BASE, trimSlash(slug)); permalink = createPath(CATEGORY_BASE, trimSlash(slug))
break; break
case 'tag': case 'tag':
permalink = createPath(TAG_BASE, trimSlash(slug)); permalink = createPath(TAG_BASE, trimSlash(slug))
break; break
case 'post': case 'post':
permalink = createPath(trimSlash(slug)); permalink = createPath(trimSlash(slug))
break; break
case 'page': case 'page':
default: default:
permalink = createPath(slug); permalink = createPath(slug)
break; break
} }
return definitivePermalink(permalink); return definitivePermalink(permalink)
}; }
/** */ /** */
export const getHomePermalink = (): string => getPermalink('/'); export const getHomePermalink = (): string => getPermalink('/')
/** */ /** */
export const getBlogPermalink = (): string => getPermalink(BLOG_BASE); export const getBlogPermalink = (): string => getPermalink(BLOG_BASE)
/** */ /** */
export const getAsset = (path: string): string => export const getAsset = (path: string): string =>
@ -98,37 +99,37 @@ export const getAsset = (path: string): string =>
[BASE_PATHNAME, path] [BASE_PATHNAME, path]
.map((el) => trimSlash(el)) .map((el) => trimSlash(el))
.filter((el) => !!el) .filter((el) => !!el)
.join('/'); .join('/')
/** */ /** */
const definitivePermalink = (permalink: string): string => createPath(BASE_PATHNAME, permalink); const definitivePermalink = (permalink: string): string => createPath(BASE_PATHNAME, permalink)
/** */ /** */
export const applyGetPermalinks = (menu: object = {}) => { export const applyGetPermalinks = (menu: object = {}) => {
if (Array.isArray(menu)) { if (Array.isArray(menu)) {
return menu.map((item) => applyGetPermalinks(item)); return menu.map((item) => applyGetPermalinks(item))
} else if (typeof menu === 'object' && menu !== null) { } else if (typeof menu === 'object' && menu !== null) {
const obj = {}; const obj = {}
for (const key in menu) { for (const key in menu) {
if (key === 'href') { if (key === 'href') {
if (typeof menu[key] === 'string') { if (typeof menu[key] === 'string') {
obj[key] = getPermalink(menu[key]); obj[key] = getPermalink(menu[key])
} else if (typeof menu[key] === 'object') { } else if (typeof menu[key] === 'object') {
if (menu[key].type === 'home') { if (menu[key].type === 'home') {
obj[key] = getHomePermalink(); obj[key] = getHomePermalink()
} else if (menu[key].type === 'blog') { } else if (menu[key].type === 'blog') {
obj[key] = getBlogPermalink(); obj[key] = getBlogPermalink()
} else if (menu[key].type === 'asset') { } else if (menu[key].type === 'asset') {
obj[key] = getAsset(menu[key].url); obj[key] = getAsset(menu[key].url)
} else if (menu[key].url) { } else if (menu[key].url) {
obj[key] = getPermalink(menu[key].url, menu[key].type); obj[key] = getPermalink(menu[key].url, menu[key].type)
} }
} }
} else { } else {
obj[key] = applyGetPermalinks(menu[key]); obj[key] = applyGetPermalinks(menu[key])
} }
} }
return obj; return obj
}
return menu
} }
return menu;
};

View File

@ -4,7 +4,8 @@
"allowJs": true, "allowJs": true,
"baseUrl": "src", "baseUrl": "src",
// hide an issue with typescript // hide an issue with typescript
"noUnusedLocals": false "noUnusedLocals": false,
"noImplicitAny": false,
}, },
"ts-node": { "ts-node": {
"esm": true "esm": true

View File

@ -1,86 +1,87 @@
import merge from 'lodash.merge'; /* eslint-disable @typescript-eslint/no-empty-interface */
import merge from 'lodash.merge'
import type { MetaData } from '~/types'; import type { MetaData } from 'types'
type Config = { interface Config {
site?: SiteConfig; site?: SiteConfig
metadata?: MetaDataConfig; metadata?: MetaDataConfig
i18n?: I18NConfig; i18n?: I18NConfig
apps?: { apps?: {
blog?: AppBlogConfig; blog?: AppBlogConfig
}; }
ui?: unknown; ui?: unknown
analytics?: unknown; analytics?: unknown
}; }
export interface SiteConfig { export interface SiteConfig {
name: string; name: string
site?: string; site?: string
base?: string; base?: string
trailingSlash?: boolean; trailingSlash?: boolean
googleSiteVerificationId?: string; googleSiteVerificationId?: string
} }
export interface MetaDataConfig extends Omit<MetaData, 'title'> { export interface MetaDataConfig extends Omit<MetaData, 'title'> {
title?: { title?: {
default: string; default: string
template: string; template: string
}; }
} }
export interface I18NConfig { export interface I18NConfig {
language: string; language: string
textDirection: string; textDirection: string
dateFormatter?: Intl.DateTimeFormat; dateFormatter?: Intl.DateTimeFormat
} }
export interface AppBlogConfig { export interface AppBlogConfig {
isEnabled: boolean; isEnabled: boolean
postsPerPage: number; postsPerPage: number
isRelatedPostsEnabled: boolean; isRelatedPostsEnabled: boolean
relatedPostsCount: number; relatedPostsCount: number
post: { post: {
isEnabled: boolean; isEnabled: boolean
permalink: string; permalink: string
robots: { robots: {
index: boolean; index: boolean
follow: boolean; follow: boolean
}; }
}; }
list: { list: {
isEnabled: boolean; isEnabled: boolean
pathname: string; pathname: string
robots: { robots: {
index: boolean; index: boolean
follow: boolean; follow: boolean
}; }
}; }
category: { category: {
isEnabled: boolean; isEnabled: boolean
pathname: string; pathname: string
robots: { robots: {
index: boolean; index: boolean
follow: boolean; follow: boolean
}; }
}; }
tag: { tag: {
isEnabled: boolean; isEnabled: boolean
pathname: string; pathname: string
robots: { robots: {
index: boolean; index: boolean
follow: boolean; follow: boolean
}; }
}; }
} }
export interface AnalyticsConfig { export interface AnalyticsConfig {
vendors: { vendors: {
googleAnalytics: { googleAnalytics: {
id?: string; id?: string
partytown?: boolean; partytown?: boolean
}; }
}; }
} }
export interface UIConfig {} export interface UIConfig {}
const DEFAULT_SITE_NAME = 'Website'; const DEFAULT_SITE_NAME = 'Website'
const getSite = (config: Config) => { const getSite = (config: Config) => {
const _default = { const _default = {
@ -90,13 +91,13 @@ const getSite = (config: Config) => {
trailingSlash: false, trailingSlash: false,
googleSiteVerificationId: '', googleSiteVerificationId: '',
}; }
return merge({}, _default, config?.site ?? {}) as SiteConfig; return merge({}, _default, config?.site ?? {}) as SiteConfig
}; }
const getMetadata = (config: Config) => { const getMetadata = (config: Config) => {
const siteConfig = getSite(config); const siteConfig = getSite(config)
const _default = { const _default = {
title: { title: {
@ -111,21 +112,21 @@ const getMetadata = (config: Config) => {
openGraph: { openGraph: {
type: 'website', type: 'website',
}, },
}; }
return merge({}, _default, config?.metadata ?? {}) as MetaDataConfig; return merge({}, _default, config?.metadata ?? {}) as MetaDataConfig
}; }
const getI18N = (config: Config) => { const getI18N = (config: Config) => {
const _default = { const _default = {
language: 'en', language: 'en',
textDirection: 'ltr', textDirection: 'ltr',
}; }
const value = merge({}, _default, config?.i18n ?? {}); const value = merge({}, _default, config?.i18n ?? {})
return value as I18NConfig; return value as I18NConfig
}; }
const getAppBlog = (config: Config) => { const getAppBlog = (config: Config) => {
const _default = { const _default = {
@ -165,18 +166,18 @@ const getAppBlog = (config: Config) => {
follow: true, follow: true,
}, },
}, },
}; }
return merge({}, _default, config?.apps?.blog ?? {}) as AppBlogConfig; return merge({}, _default, config?.apps?.blog ?? {}) as AppBlogConfig
}; }
const getUI = (config: Config) => { const getUI = (config: Config) => {
const _default = { const _default = {
theme: 'system', theme: 'system',
}; }
return merge({}, _default, config?.ui ?? {}); return merge({}, _default, config?.ui ?? {})
}; }
const getAnalytics = (config: Config) => { const getAnalytics = (config: Config) => {
const _default = { const _default = {
@ -186,10 +187,10 @@ const getAnalytics = (config: Config) => {
partytown: true, partytown: true,
}, },
}, },
}; }
return merge({}, _default, config?.analytics ?? {}) as AnalyticsConfig; return merge({}, _default, config?.analytics ?? {}) as AnalyticsConfig
}; }
export default (config: Config) => ({ export default (config: Config) => ({
SITE: getSite(config), SITE: getSite(config),
@ -198,4 +199,4 @@ export default (config: Config) => ({
APP_BLOG: getAppBlog(config), APP_BLOG: getAppBlog(config),
UI: getUI(config), UI: getUI(config),
ANALYTICS: getAnalytics(config), ANALYTICS: getAnalytics(config),
}); })