import { z } from 'astro/zod'; const isProd = (): boolean => import.meta.env.MODE === 'production' || process.env.NODE_ENV === 'production'; // The type of the options, use zod for better validation. const Options = z .object({ local: z .object({ port: z.number(), }) .transform((local) => ({ ...local, website: `http://localhost:${local.port}` })), title: z.string().max(40), website: z .string() .url() .refine((u) => !u.endsWith('/')) .readonly(), description: z.string().max(100), keywords: z.array(z.string()), author: z.object({ name: z.string(), email: z.string().email(), url: z.string().url() }), navigation: z.array(z.object({ text: z.string().trim(), link: z.string(), target: z.string().optional() })), socials: z.array( z.object({ name: z.string(), icon: z.string(), type: z.enum(['link', 'qrcode']), title: z.string().optional(), link: z.string().url(), }), ), settings: z.object({ footer: z.object({ initialYear: z.number().max(2024), icpNo: z.string().optional(), moeIcpNo: z.string().optional(), astroBadge: z.boolean().default(true), }), locale: z.string().optional().default('zh-CN'), timeZone: z.string().optional().default('Asia/Shanghai'), timeFormat: z.string().optional().default('yyyy-MM-dd HH:mm:ss'), twitter: z.string(), assetPrefix: z .string() .url() .refine((u) => !u.endsWith('/')) .readonly(), post: z.object({ sort: z.enum(['asc', 'desc']), feature: z.array(z.string()).min(3).optional(), }), pagination: z.object({ posts: z.number().optional().default(5), category: z.number().optional().default(7), tags: z.number().optional().default(7), search: z.number().optional().default(7), }), feed: z.object({ full: z.boolean().optional().default(true), size: z.number().optional().default(20), }), sidebar: z.object({ search: z.boolean().default(false), post: z.number().default(6), comment: z.number().default(0), tag: z.number().default(20), calendar: z.boolean().default(false), }), comments: z.object({ enable: z.boolean().optional().default(true), size: z.number().default(10).readonly(), avatar: z.object({ mirror: z.string().url().readonly(), size: z.number(), }), }), toc: z.object({ minHeadingLevel: z.number().optional().default(2), maxHeadingLevel: z.number().optional().default(3), }), }), thumbnail: z .function() .args(z.object({ src: z.string().min(1), width: z.number().or(z.string()), height: z.number().or(z.string()) })) .returns(z.string()), }) .transform((opts) => { const assetsPrefix = (): string => (isProd() ? opts.settings.assetPrefix : opts.local.website); return { ...opts, // Monkey patch for the issue https://github.com/withastro/astro/issues/11282 // No need to fallback to the import.meta.env.PROD I think. isProd, // Given that the import.meta.env.ASSETS_PREFIX has two types. // I have to use this uniform method instead. assetsPrefix, defaultOpenGraph: (): string => `${assetsPrefix()}/images/open-graph.png`, }; }) .refine( (options) => options.settings.toc.minHeadingLevel <= options.settings.toc.maxHeadingLevel, 'Invalid toc setting, the minHeadingLevel should bellow the maxHeadingLevel', ); const options: z.input<typeof Options> = { local: { port: 4321, }, title: '且听书吟', website: 'https://yufan.me', description: '诗与梦想的远方', keywords: ['雨帆', '且听书吟', 'syhily', 'amehochan', 'yufan'], author: { name: '雨帆', email: 'syhily@gmail.com', url: 'https://yufan.me', }, navigation: [ { text: '首页', link: '/', }, { text: '分类', link: '/categories', }, { text: '关于', link: '/about', }, { text: '留言', link: '/guestbook', }, { text: '友链', link: '/links', }, ], socials: [ { name: 'GitHub', icon: 'icon-github-fill', type: 'link', link: 'https://github.com/syhily', }, { name: 'Follow', icon: '<svg viewBox="0 0 83 80"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(0.6377, 0.6948)" fill="#FFFFFF" fill-rule="nonzero"><path d="M70.9713,-7.10542736e-15 L20.9446,-7.10542736e-15 C14.895,-7.10542736e-15 9.9907,4.8998 9.9907,10.944 C9.9907,16.9882 14.895,21.888 20.9446,21.888 L70.9713,21.888 C77.0213,21.888 81.9253,16.9882 81.9253,10.944 C81.9253,4.8998 77.0213,-7.10542736e-15 70.9713,-7.10542736e-15 Z" id="Path"></path><path d="M44.2971,28.4541 L10.9539,28.4541 C4.9042,28.4541 -7.10542736e-15,33.3539 -7.10542736e-15,39.3981 C-7.10542736e-15,45.4423 4.9042,50.3421 10.9539,50.3421 L44.2971,50.3421 C50.3468,50.3421 55.2511,45.4423 55.2511,39.3981 C55.2511,33.3539 50.3468,28.4541 44.2971,28.4541 Z" id="Path"></path><path d="M47.5231,67.8762 C47.5231,61.8324 42.6188,56.9326 36.5691,56.9326 C30.5195,56.9326 25.6152,61.8324 25.6152,67.8762 C25.6152,73.9212 30.5195,78.8202 36.5691,78.8202 C42.6188,78.8202 47.5231,73.9212 47.5231,67.8762 Z" id="Path"></path></g></g></svg>', type: 'link', link: 'https://app.follow.is/share/feeds/54772566650461214', }, { name: '微信', icon: 'icon-wechat', type: 'qrcode', title: '扫码加我微信好友', link: 'https://u.wechat.com/EBpmuKmrVz4YVFnoCJdnruA', }, ], settings: { footer: { initialYear: 2011, icpNo: '皖ICP备2021002315号-2', moeIcpNo: '萌ICP备20200318号', astroBadge: true, }, locale: 'zh-CN', timeZone: 'Asia/Shanghai', timeFormat: 'yyyy-MM-dd', twitter: 'amehochan', assetPrefix: 'https://cat.yufan.me', post: { sort: 'desc', }, pagination: { posts: 6, category: 7, tags: 7, search: 7, }, feed: { full: true, size: 10, }, sidebar: { search: true, post: 5, comment: 5, tag: 10, calendar: false, }, comments: { enable: true, size: 10, avatar: { mirror: 'https://weavatar.com/avatar', size: 120, }, }, toc: { minHeadingLevel: 2, maxHeadingLevel: 3, }, }, thumbnail: ({ src, width, height }) => { if (src.endsWith('.svg')) { return src; } if (isProd()) { // Add upyun thumbnail support. return `${src}!upyun520/both/${width}x${height}/format/webp/quality/100/unsharp/true/progressive/true`; } // See https://docs.astro.build/en/reference/image-service-reference/#local-services // Remember to add the localhost to you image service settings. return `http://localhost:4321/_image?href=${src}&w=${width}&h=${height}&f=webp&q=100`; }, }; export default Options.parse(options);