Migrate to typescript
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
.github
|
.github
|
||||||
|
types.generated.d.ts
|
@ -3,7 +3,7 @@
|
|||||||
<img src="lighthouse-score.png" align="right"
|
<img src="lighthouse-score.png" align="right"
|
||||||
alt="AstroWind Lighthouse Score" width="100" height="358">
|
alt="AstroWind Lighthouse Score" width="100" height="358">
|
||||||
|
|
||||||
**AstroWind** is a free and open-source template to make your website using **[Astro](https://astro.build/) + [Tailwind CSS](https://tailwindcss.com/)**. Ready to start a new project and designed taking into account best practices. 🌟 **Most *starred* & *forked* Astro theme in 2022**.
|
**AstroWind** is a free and open-source template to make your website using **[Astro](https://astro.build/) + [Tailwind CSS](https://tailwindcss.com/)**. Ready to start a new project and designed taking into account best practices. 🌟 **Most _starred_ & _forked_ Astro theme in 2022**.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ Inside AstroWind template, you'll see the following folders and files:
|
|||||||
│ | | ├── post-slug-1.md
|
│ | | ├── post-slug-1.md
|
||||||
│ | | ├── post-slug-2.mdx
|
│ | | ├── post-slug-2.mdx
|
||||||
│ | | └── ...
|
│ | | └── ...
|
||||||
│ | └-- config.js
|
│ | └-- config.ts
|
||||||
│ ├── layouts/
|
│ ├── layouts/
|
||||||
│ | |── BaseLayout.astro
|
│ | |── BaseLayout.astro
|
||||||
│ | └── ...
|
│ | └── ...
|
||||||
@ -104,7 +104,7 @@ Inside AstroWind template, you'll see the following folders and files:
|
|||||||
| | | └── [...page].astro
|
| | | └── [...page].astro
|
||||||
│ | ├── index.astro
|
│ | ├── index.astro
|
||||||
| | ├── 404.astro
|
| | ├── 404.astro
|
||||||
| | └-- rss.xml.js
|
| | └-- rss.xml.ts
|
||||||
│ ├── utils/
|
│ ├── utils/
|
||||||
│ └── config.mjs
|
│ └── config.mjs
|
||||||
├── package.json
|
├── package.json
|
||||||
|
@ -17,10 +17,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown:hover .dropdown-menu {
|
.dropdown:hover .dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[astro-icon].icon-light > * {
|
[astro-icon].icon-light > * {
|
||||||
stroke-width: 1.2;
|
stroke-width: 1.2;
|
||||||
}
|
}
|
||||||
@ -34,4 +33,4 @@
|
|||||||
|
|
||||||
[data-aw-toggle-menu].expanded g > path:last-child {
|
[data-aw-toggle-menu].expanded g > path:last-child {
|
||||||
@apply rotate-45 translate-y-[-8px] translate-x-[14px];
|
@apply rotate-45 translate-y-[-8px] translate-x-[14px];
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
import { getRelativeLink } from '~/utils/permalinks';
|
import { getRelativeLink } from '~/utils/permalinks';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
prevUrl: string;
|
||||||
|
nextUrl: string;
|
||||||
|
prevText?: string;
|
||||||
|
nextText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
|
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -18,12 +26,7 @@ const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } =
|
|||||||
<p class="ml-2">{prevText}</p>
|
<p class="ml-2">{prevText}</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href={getRelativeLink(nextUrl)} class={`btn btn-ghost px-3 ${!nextUrl ? 'invisible' : ''}`}>
|
||||||
href={getRelativeLink(nextUrl)}
|
|
||||||
class={`btn btn-ghost px-3 ${
|
|
||||||
!nextUrl ? 'invisible' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div class="flex flex-row align-middle">
|
<div class="flex flex-row align-middle">
|
||||||
<span class="mr-2">{nextText}</span>
|
<span class="mr-2">{nextText}</span>
|
||||||
<Icon name="tabler:arrow-right" class="w-6 h-6" />
|
<Icon name="tabler:arrow-right" class="w-6 h-6" />
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
text: string;
|
||||||
|
url: string;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const { text, url, class: className = 'inline-block' } = Astro.props;
|
const { text, url, class: className = 'inline-block' } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
---
|
---
|
||||||
import { getPermalink } from '~/utils/permalinks';
|
import { getPermalink } from '~/utils/permalinks';
|
||||||
|
|
||||||
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
tags: Post['tags'];
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const { tags, class: className = 'text-sm' } = Astro.props;
|
const { tags, class: className = 'text-sm' } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
---
|
---
|
||||||
import Item from '~/components/blog/GridItem.astro';
|
import Item from '~/components/blog/GridItem.astro';
|
||||||
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
posts: Array<Post>;
|
||||||
|
}
|
||||||
|
|
||||||
const { posts } = Astro.props;
|
const { posts } = Astro.props;
|
||||||
---
|
---
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
---
|
---
|
||||||
import { Picture } from '@astrojs/image/components'
|
import { Picture } from '@astrojs/image/components';
|
||||||
|
|
||||||
import { findImage } from '~/utils/images';
|
import { findImage } from '~/utils/images';
|
||||||
import { getPermalink } from '~/utils/permalinks';
|
import { getPermalink } from '~/utils/permalinks';
|
||||||
|
|
||||||
const { post } = Astro.props;
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
post: Post;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { post } = Astro.props;
|
||||||
const image = await findImage(post.image);
|
const image = await findImage(post.image);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
---
|
---
|
||||||
import Item from '~/components/blog/ListItem.astro';
|
import Item from '~/components/blog/ListItem.astro';
|
||||||
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
posts: Array<Post>;
|
||||||
|
}
|
||||||
|
|
||||||
const { posts } = Astro.props;
|
const { posts } = Astro.props;
|
||||||
---
|
---
|
||||||
|
@ -6,8 +6,13 @@ import { getPermalink } from '~/utils/permalinks';
|
|||||||
import { findImage } from '~/utils/images';
|
import { findImage } from '~/utils/images';
|
||||||
import { getFormattedDate } from '~/utils/utils';
|
import { getFormattedDate } from '~/utils/utils';
|
||||||
|
|
||||||
const { post } = Astro.props;
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
post: Post;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { post } = Astro.props;
|
||||||
const image = await findImage(post.image);
|
const image = await findImage(post.image);
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -47,7 +52,7 @@ const image = await findImage(post.image);
|
|||||||
<footer class="mt-4">
|
<footer class="mt-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-gray-500 dark:text-slate-400">
|
<span class="text-gray-500 dark:text-slate-400">
|
||||||
<time datetime={post.publishDate}>{getFormattedDate(post.publishDate)}</time> ~
|
<time datetime={String(post.publishDate)}>{getFormattedDate(post.publishDate)}</time> ~
|
||||||
{Math.ceil(post.readingTime)} min read
|
{Math.ceil(post.readingTime)} min read
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { Picture } from '@astrojs/image/components'
|
import { Picture } from '@astrojs/image/components';
|
||||||
import PostTags from '~/components/atoms/Tags.astro';
|
import PostTags from '~/components/atoms/Tags.astro';
|
||||||
import SocialShare from '~/components/atoms/SocialShare.astro';
|
import SocialShare from '~/components/atoms/SocialShare.astro';
|
||||||
|
|
||||||
import { getFormattedDate } from '~/utils/utils';
|
import { getFormattedDate } from '~/utils/utils';
|
||||||
|
|
||||||
|
import type { Post } from '~/utils/posts';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
post: Post;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
const { post, url } = Astro.props;
|
const { post, url } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -12,8 +19,9 @@ const { post, url } = Astro.props;
|
|||||||
<article>
|
<article>
|
||||||
<header class={post.image ? 'text-center' : ''}>
|
<header class={post.image ? 'text-center' : ''}>
|
||||||
<p class="px-4 sm:px-6 max-w-3xl mx-auto">
|
<p class="px-4 sm:px-6 max-w-3xl mx-auto">
|
||||||
<time datetime={post.publishDate}>{getFormattedDate(post.publishDate)}</time> ~ {Math.ceil(post.readingTime)} min
|
<time datetime={String(post.publishDate)}>{getFormattedDate(post.publishDate)}</time> ~ {
|
||||||
read
|
Math.ceil(post.readingTime)
|
||||||
|
} min read
|
||||||
</p>
|
</p>
|
||||||
<h1
|
<h1
|
||||||
class="px-4 sm:px-6 max-w-3xl mx-auto text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-8 font-heading"
|
class="px-4 sm:px-6 max-w-3xl mx-auto text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-8 font-heading"
|
||||||
@ -27,9 +35,9 @@ const { post, url } = Astro.props;
|
|||||||
class="max-w-full lg:max-w-6xl mx-auto mt-4 mb-6 sm:rounded-md bg-gray-400 dark:bg-slate-700"
|
class="max-w-full lg:max-w-6xl mx-auto mt-4 mb-6 sm:rounded-md bg-gray-400 dark:bg-slate-700"
|
||||||
widths={[400, 900]}
|
widths={[400, 900]}
|
||||||
sizes="(max-width: 900px) 400px, 900px"
|
sizes="(max-width: 900px) 400px, 900px"
|
||||||
alt={post.description}
|
alt={post.description || ''}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
aspectRatio={16/9}
|
aspectRatio={16 / 9}
|
||||||
width={900}
|
width={900}
|
||||||
height={506}
|
height={506}
|
||||||
/>
|
/>
|
||||||
@ -43,15 +51,20 @@ const { post, url } = Astro.props;
|
|||||||
<div
|
<div
|
||||||
class="container mx-auto px-6 sm:px-6 max-w-3xl prose prose-lg lg:prose-xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary-600 dark:prose-a:text-primary-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
|
class="container mx-auto px-6 sm:px-6 max-w-3xl prose prose-lg lg:prose-xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary-600 dark:prose-a:text-primary-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
|
||||||
>
|
>
|
||||||
{post.Content ? <post.Content /> : <Fragment set:html={post.body} />}
|
{
|
||||||
|
post.Content ? (
|
||||||
|
<>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<post.Content />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Fragment set:html={post.content} />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="container mx-auto px-6 sm:px-6 max-w-3xl mt-8 flex justify-between flex-col sm:flex-row">
|
<div class="container mx-auto px-6 sm:px-6 max-w-3xl mt-8 flex justify-between flex-col sm:flex-row">
|
||||||
<PostTags tags={post.tags} class="mr-5" />
|
<PostTags tags={post.tags} class="mr-5" />
|
||||||
<SocialShare
|
<SocialShare url={url} text={post.title} class="mt-5 sm:mt-1 align-middle text-gray-500 dark:text-slate-600" />
|
||||||
url={url}
|
|
||||||
text={post.title}
|
|
||||||
class="mt-5 sm:mt-1 align-middle text-gray-500 dark:text-slate-600"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
label?: string;
|
||||||
|
class?: string;
|
||||||
|
iconClass?: string;
|
||||||
|
iconName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
label = 'Toggle Menu',
|
label = 'Toggle Menu',
|
||||||
class:
|
class:
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
label?: string;
|
||||||
|
class?: string;
|
||||||
|
iconClass?: string;
|
||||||
|
iconName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
label = 'Toggle between Dark and Light mode',
|
label = 'Toggle between Dark and Light mode',
|
||||||
class:
|
class:
|
||||||
|
@ -13,12 +13,7 @@ import { getHomePermalink } from '~/utils/permalinks';
|
|||||||
<p class="mt-4 mb-8 text-lg text-gray-600 dark:text-slate-400">
|
<p class="mt-4 mb-8 text-lg text-gray-600 dark:text-slate-400">
|
||||||
But dont worry, you can find plenty of other things on our homepage.
|
But dont worry, you can find plenty of other things on our homepage.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a rel="noopener noreferrer" href={getHomePermalink()} class="btn ml-4">Back to homepage</a>
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={getHomePermalink()}
|
|
||||||
class="btn ml-4"
|
|
||||||
>Back to homepage</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -37,16 +37,19 @@ import { getHomePermalink, getBlogPermalink, getPermalink, getRelativeLink } fro
|
|||||||
<li class="">
|
<li class="">
|
||||||
<a
|
<a
|
||||||
class="rounded-t md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap"
|
class="rounded-t md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap"
|
||||||
href="#">Features</a>
|
href="#">Features</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a class="md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap" href="#"
|
<a class="md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap" href="#"
|
||||||
>Profile</a>
|
>Profile</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a
|
<a
|
||||||
class="rounded-b md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap"
|
class="rounded-b md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-4 block whitespace-no-wrap"
|
||||||
href="#">Pricing</a>
|
href="#">Pricing</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
import { Picture } from '@astrojs/image/components'
|
import { Picture } from '@astrojs/image/components';
|
||||||
---
|
---
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
@ -10,16 +10,15 @@ import { Picture } from '@astrojs/image/components'
|
|||||||
<h1 class="text-5xl md:text-[3.50rem] font-bold leading-tighter tracking-tighter mb-4 font-heading">
|
<h1 class="text-5xl md:text-[3.50rem] font-bold leading-tighter tracking-tighter mb-4 font-heading">
|
||||||
Your website with
|
Your website with
|
||||||
<span>Astro</span> +
|
<span>Astro</span> +
|
||||||
<span
|
<span class="sm:whitespace-nowrap">Tailwind CSS</span>
|
||||||
class="sm:whitespace-nowrap"
|
|
||||||
>Tailwind CSS</span
|
|
||||||
>
|
|
||||||
</h1>
|
</h1>
|
||||||
<div class="max-w-3xl mx-auto">
|
<div class="max-w-3xl mx-auto">
|
||||||
<p class="text-xl text-gray-600 mb-8 dark:text-slate-400">
|
<p class="text-xl text-gray-600 mb-8 dark:text-slate-400">
|
||||||
<span class="font-semibold underline decoration-wavy decoration-1 decoration-primary-600 underline-offset-2">AstroWind</span> is a production ready template to start your new website using <em>Astro</em> + <em>Tailwind CSS</em>. It has been
|
<span class="font-semibold underline decoration-wavy decoration-1 decoration-primary-600 underline-offset-2"
|
||||||
designed following Best Practices, SEO, Accessibility, <span class="inline sm:hidden">...</span><span
|
>AstroWind</span
|
||||||
class="hidden sm:inline"
|
> is a production ready template to start your new website using <em>Astro</em> + <em>Tailwind CSS</em>. It
|
||||||
|
has been designed following Best Practices, SEO, Accessibility, <span class="inline sm:hidden">...</span
|
||||||
|
><span class="hidden sm:inline"
|
||||||
>Dark Mode, Great Page Speed, image optimization, sitemap generation and more.</span
|
>Dark Mode, Great Page Speed, image optimization, sitemap generation and more.</span
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
@ -35,10 +34,7 @@ import { Picture } from '@astrojs/image/components'
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full sm:w-auto">
|
<div class="flex w-full sm:w-auto">
|
||||||
<a
|
<a class="btn w-full" href="#features">Learn more</a>
|
||||||
class="btn w-full"
|
|
||||||
href="#features">Learn more</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +47,7 @@ import { Picture } from '@astrojs/image/components'
|
|||||||
widths={[400, 768, 1480]}
|
widths={[400, 768, 1480]}
|
||||||
sizes="(max-width: 767px) 400px, (max-width: 1479px) 768px, 1480px"
|
sizes="(max-width: 767px) 400px, (max-width: 1479px) 768px, 1480px"
|
||||||
alt="Hero Image"
|
alt="Hero Image"
|
||||||
aspectRatio={1480/833}
|
aspectRatio={1480 / 833}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
width={1480}
|
width={1480}
|
||||||
height={833}
|
height={833}
|
||||||
|
@ -4,7 +4,8 @@ import { Icon } from 'astro-icon';
|
|||||||
|
|
||||||
<section class="bg-primary-100 dark:bg-slate-800">
|
<section class="bg-primary-100 dark:bg-slate-800">
|
||||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-4 text-md text-center font-medium">
|
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-4 text-md text-center font-medium">
|
||||||
<span class="font-bold"> <Icon name="tabler:info-square" class="w-5 h-5 inline-block align-text-bottom" /> Philosophy:</span> Simplicity, Best
|
<span class="font-bold">
|
||||||
Practices and High Performance
|
<Icon name="tabler:info-square" class="w-5 h-5 inline-block align-text-bottom" /> Philosophy:</span
|
||||||
|
> Simplicity, Best Practices and High Performance
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon';
|
import { Icon } from 'astro-icon';
|
||||||
import { Picture } from '@astrojs/image/components'
|
import { Picture } from '@astrojs/image/components';
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-6xl">
|
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-6xl">
|
||||||
@ -64,7 +64,9 @@ import { Picture } from '@astrojs/image/components'
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex flex-col items-center mr-4">
|
<div class="flex flex-col items-center mr-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-center w-10 h-10 rounded-full border-primary-600 border-2 bg-primary-600">
|
<div
|
||||||
|
class="flex items-center justify-center w-10 h-10 rounded-full border-primary-600 border-2 bg-primary-600"
|
||||||
|
>
|
||||||
<Icon name="tabler:check" class="w-6 h-6 text-white dark:text-slate-200" />
|
<Icon name="tabler:check" class="w-6 h-6 text-white dark:text-slate-200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
1
src/env.d.ts
vendored
Normal file
1
src/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="astro/client" />
|
@ -18,7 +18,7 @@ export async function getStaticPaths({ paginate }) {
|
|||||||
typeof post.category === 'string' && categories.add(post.category.toLowerCase());
|
typeof post.category === 'string' && categories.add(post.category.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(categories).map((category) =>
|
return Array.from(categories).map((category: string) =>
|
||||||
paginate(
|
paginate(
|
||||||
posts.filter((post) => typeof post.category === 'string' && category === post.category.toLowerCase()),
|
posts.filter((post) => typeof post.category === 'string' && category === post.category.toLowerCase()),
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ export async function getStaticPaths({ paginate }) {
|
|||||||
Array.isArray(post.tags) && post.tags.map((tag) => tags.add(tag.toLowerCase()));
|
Array.isArray(post.tags) && post.tags.map((tag) => tags.add(tag.toLowerCase()));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(tags).map((tag) =>
|
return Array.from(tags).map((tag: string) =>
|
||||||
paginate(
|
paginate(
|
||||||
posts.filter((post) => Array.isArray(post.tags) && post.tags.find((elem) => elem.toLowerCase() === tag)),
|
posts.filter((post) => Array.isArray(post.tags) && post.tags.find((elem) => elem.toLowerCase() === tag)),
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
|
|||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getProjectRootDir = () => {
|
export const getProjectRootDir = (): string => {
|
||||||
const mode = import.meta.env.MODE;
|
const mode = import.meta.env.MODE;
|
||||||
|
|
||||||
return mode === 'production' ? path.join(__dirname, '../') : path.join(__dirname, '../../');
|
return mode === 'production' ? path.join(__dirname, '../') : path.join(__dirname, '../../');
|
||||||
@ -13,10 +13,6 @@ export const getProjectRootDir = () => {
|
|||||||
const __srcFolder = path.join(getProjectRootDir(), '/src');
|
const __srcFolder = path.join(getProjectRootDir(), '/src');
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getRelativeUrlByFilePath = (filepath) => {
|
export const getRelativeUrlByFilePath = (filepath: string): string | URL => {
|
||||||
if (filepath) {
|
return filepath.replace(__srcFolder, '');
|
||||||
return filepath.replace(__srcFolder, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
@ -1,5 +1,5 @@
|
|||||||
const load = async function () {
|
const load = async function () {
|
||||||
let images = [];
|
let images: Record<string, () => Promise<unknown>> | undefined = undefined;
|
||||||
try {
|
try {
|
||||||
images = import.meta.glob('~/assets/images/**');
|
images = import.meta.glob('~/assets/images/**');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -17,7 +17,7 @@ export const fetchLocalImages = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const findImage = async (imagePath) => {
|
export const findImage = async (imagePath?: string) => {
|
||||||
if (typeof imagePath !== 'string') {
|
if (typeof imagePath !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ import slugify from 'limax';
|
|||||||
|
|
||||||
import { SITE, BLOG } from '~/config.mjs';
|
import { SITE, BLOG } from '~/config.mjs';
|
||||||
|
|
||||||
const trim = (str = "", ch) => {
|
const trim = (str = '', ch?: string) => {
|
||||||
let start = 0,
|
let start = 0,
|
||||||
end = str.length || 0;
|
end = str.length || 0;
|
||||||
while (start < end && str[start] === ch) ++start;
|
while (start < end && str[start] === ch) ++start;
|
||||||
@ -18,7 +18,7 @@ const createPath = (...params) => {
|
|||||||
|
|
||||||
const basePathname = trimSlash(SITE.basePathname);
|
const basePathname = trimSlash(SITE.basePathname);
|
||||||
|
|
||||||
export const cleanSlug = (text) => slugify(trimSlash(text));
|
export const cleanSlug = (text: string) => slugify(trimSlash(text));
|
||||||
|
|
||||||
export const BLOG_BASE = cleanSlug(BLOG?.blog?.pathname);
|
export const BLOG_BASE = cleanSlug(BLOG?.blog?.pathname);
|
||||||
export const POST_BASE = cleanSlug(BLOG?.post?.pathname);
|
export const POST_BASE = cleanSlug(BLOG?.post?.pathname);
|
||||||
@ -29,7 +29,7 @@ export const TAG_BASE = cleanSlug(BLOG?.tag?.pathname);
|
|||||||
export const getCanonical = (path = '') => new URL(path, SITE.origin);
|
export const getCanonical = (path = '') => new URL(path, SITE.origin);
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getPermalink = (slug = '', type = 'page') => {
|
export const getPermalink = (slug = '', type = 'page'): string => {
|
||||||
const _slug = cleanSlug(slug);
|
const _slug = cleanSlug(slug);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -49,15 +49,15 @@ export const getPermalink = (slug = '', type = 'page') => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getHomePermalink = () => {
|
export const getHomePermalink = (): string => {
|
||||||
const permalink = getPermalink();
|
const permalink = getPermalink();
|
||||||
return permalink !== '/' ? permalink + '/' : permalink;
|
return permalink !== '/' ? permalink + '/' : permalink;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getRelativeLink = (link = "") => {
|
export const getRelativeLink = (link = ''): string => {
|
||||||
return createPath(basePathname, trimSlash(link));
|
return createPath(basePathname, trimSlash(link));
|
||||||
}
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const getBlogPermalink = () => getPermalink(BLOG_BASE);
|
export const getBlogPermalink = (): string => getPermalink(BLOG_BASE);
|
@ -1,6 +1,32 @@
|
|||||||
import { getCollection, getEntry } from 'astro:content';
|
import { getCollection, getEntry } from 'astro:content';
|
||||||
|
import type { CollectionEntry } from 'astro:content';
|
||||||
|
|
||||||
const getNormalizedPost = async (post) => {
|
export interface Post {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
publishDate: Date;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
image?: string;
|
||||||
|
|
||||||
|
canonical?: string;
|
||||||
|
permalink?: string;
|
||||||
|
|
||||||
|
draft?: boolean;
|
||||||
|
|
||||||
|
excerpt?: string;
|
||||||
|
category?: string;
|
||||||
|
tags?: Array<string>;
|
||||||
|
authors?: Array<string>;
|
||||||
|
|
||||||
|
Content: unknown;
|
||||||
|
content?: string;
|
||||||
|
readingTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNormalizedPost = async (post: CollectionEntry<'blog'>): Promise<Post> => {
|
||||||
const { id, slug, data } = post;
|
const { id, slug, data } = post;
|
||||||
const { Content, injectedFrontmatter } = await post.render();
|
const { Content, injectedFrontmatter } = await post.render();
|
||||||
|
|
||||||
@ -16,7 +42,7 @@ const getNormalizedPost = async (post) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = async function () {
|
const load = async function (): Promise<Array<Post>> {
|
||||||
const posts = await getCollection('blog');
|
const posts = await getCollection('blog');
|
||||||
const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post));
|
const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post));
|
||||||
|
|
||||||
@ -27,23 +53,25 @@ const load = async function () {
|
|||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
let _posts;
|
let _posts: Array<Post>;
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const fetchPosts = async () => {
|
export const fetchPosts = async (): Promise<Array<Post>> => {
|
||||||
_posts = _posts || load();
|
if (!_posts) {
|
||||||
|
_posts = await load();
|
||||||
|
}
|
||||||
|
|
||||||
return await _posts;
|
return _posts;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const findPostsBySlugs = async (slugs) => {
|
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, slug) {
|
return slugs.reduce(function (r: Array<Post>, slug: string) {
|
||||||
posts.some(function (post) {
|
posts.some(function (post: Post) {
|
||||||
return slug === post.slug && r.push(post);
|
return slug === post.slug && r.push(post);
|
||||||
});
|
});
|
||||||
return r;
|
return r;
|
||||||
@ -51,11 +79,11 @@ export const findPostsBySlugs = async (slugs) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const findPostsByIds = async (ids) => {
|
export const findPostsByIds = async (ids: Array<string>): Promise<Array<Post>> => {
|
||||||
if (!Array.isArray(ids)) return [];
|
if (!Array.isArray(ids)) return [];
|
||||||
|
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
ids.map(async (id) => {
|
ids.map(async (id: never) => {
|
||||||
const post = await getEntry('blog', id);
|
const post = await getEntry('blog', id);
|
||||||
return await getNormalizedPost(post);
|
return await getNormalizedPost(post);
|
||||||
})
|
})
|
||||||
@ -63,7 +91,7 @@ export const findPostsByIds = async (ids) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
export const findLatestPosts = async ({ count }) => {
|
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();
|
||||||
|
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||||
/** */
|
/** */
|
||||||
export const getFormattedDate = (date) =>
|
export const getFormattedDate = (date) =>
|
||||||
date
|
date
|
||||||
@ -6,4 +7,4 @@ export const getFormattedDate = (date) =>
|
|||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
})
|
})
|
||||||
: '';
|
: '';
|
Reference in New Issue
Block a user