cosmolo 0.3.0
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.
- package/package.json +28 -0
- package/src/articles.ts +124 -0
- package/src/categories.ts +35 -0
- package/src/cli/generate.ts +190 -0
- package/src/cli/index.ts +22 -0
- package/src/cli/init.ts +173 -0
- package/src/config.ts +12 -0
- package/src/index.ts +34 -0
- package/src/loaders.ts +135 -0
- package/src/markdown.ts +74 -0
- package/src/pages.ts +23 -0
- package/src/plugin.ts +67 -0
- package/src/types.ts +65 -0
- package/src/virtual.d.ts +8 -0
- package/templates/full/lib/components/Pagination.svelte +68 -0
- package/templates/full/routes/(pages)/[slug]/+page.svelte +14 -0
- package/templates/full/routes/+page.svelte +84 -0
- package/templates/full/routes/articles/[slug]/+page.svelte +92 -0
- package/templates/full/routes/categories/[slug]/+page.svelte +58 -0
- package/templates/full/routes/tags/[tag]/+page.svelte +56 -0
- package/templates/shared/cosmolo.config.ts +10 -0
- package/templates/shared/routes/(pages)/[slug]/+page.server.ts +5 -0
- package/templates/shared/routes/+page.server.ts +4 -0
- package/templates/shared/routes/articles/[slug]/+page.server.ts +21 -0
- package/templates/shared/routes/categories/[slug]/+page.server.ts +5 -0
- package/templates/shared/routes/rss.xml/+server.ts +55 -0
- package/templates/shared/routes/sitemap.xml/+server.ts +30 -0
- package/templates/shared/routes/tags/[tag]/+page.server.ts +5 -0
- package/templates/shared/vite.config.ts +8 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getCategoryLabel } from 'cosmolo';
|
|
3
|
+
import Pagination from '$lib/components/Pagination.svelte';
|
|
4
|
+
import config from '../../../../cosmolo.config';
|
|
5
|
+
import type { PageData } from './$types';
|
|
6
|
+
|
|
7
|
+
const { data }: { data: PageData } = $props();
|
|
8
|
+
|
|
9
|
+
const perPage = 10;
|
|
10
|
+
let currentPage = $state(1);
|
|
11
|
+
|
|
12
|
+
$effect(() => {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
14
|
+
data.tag;
|
|
15
|
+
currentPage = 1;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const paginated = $derived(
|
|
19
|
+
data.articles.slice((currentPage - 1) * perPage, currentPage * perPage)
|
|
20
|
+
);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<section>
|
|
24
|
+
<div class="container">
|
|
25
|
+
<header>
|
|
26
|
+
<p>Tag</p>
|
|
27
|
+
<h1>#{data.tag}</h1>
|
|
28
|
+
<p>{data.articles.length} article{data.articles.length !== 1 ? 's' : ''}</p>
|
|
29
|
+
</header>
|
|
30
|
+
|
|
31
|
+
<ul>
|
|
32
|
+
{#each paginated as article}
|
|
33
|
+
<li>
|
|
34
|
+
<a href="/articles/{article.slug}">
|
|
35
|
+
<span>{getCategoryLabel(config, article.category)}</span>
|
|
36
|
+
{#if article.date}
|
|
37
|
+
<time datetime={article.date}>{article.date}</time>
|
|
38
|
+
{/if}
|
|
39
|
+
<h2>{article.title}</h2>
|
|
40
|
+
<p>{article.excerpt}</p>
|
|
41
|
+
</a>
|
|
42
|
+
</li>
|
|
43
|
+
{/each}
|
|
44
|
+
</ul>
|
|
45
|
+
|
|
46
|
+
<Pagination
|
|
47
|
+
total={data.articles.length}
|
|
48
|
+
{perPage}
|
|
49
|
+
{currentPage}
|
|
50
|
+
onPageChange={(p) => {
|
|
51
|
+
currentPage = p;
|
|
52
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolveConfig } from 'cosmolo';
|
|
2
|
+
|
|
3
|
+
const config = resolveConfig({
|
|
4
|
+
// articlesDir: 'src/content/articles',
|
|
5
|
+
// pagesDir: 'src/content/pages',
|
|
6
|
+
// siteConfigPath: 'config/site.json',
|
|
7
|
+
// categoriesConfigPath: 'config/categories.json',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export default config;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createArticleLoader, createArticleEntries } from 'cosmolo';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import config from '../../../../cosmolo.config';
|
|
4
|
+
|
|
5
|
+
function getUpdatedAt(slug: string): string {
|
|
6
|
+
for (const ext of ['md', 'svx']) {
|
|
7
|
+
try {
|
|
8
|
+
const result = execSync(
|
|
9
|
+
`git log -1 --format=%cI -- "src/content/articles/${slug}.${ext}"`,
|
|
10
|
+
{ encoding: 'utf-8' }
|
|
11
|
+
).trim();
|
|
12
|
+
if (result) return result.split('T')[0];
|
|
13
|
+
} catch {
|
|
14
|
+
// git not available or file not tracked
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const entries = createArticleEntries(config);
|
|
21
|
+
export const load = createArticleLoader(config, { getUpdatedAt });
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getArticles, loadSiteConfig } from 'cosmolo';
|
|
2
|
+
import config from '../../../cosmolo.config';
|
|
3
|
+
|
|
4
|
+
export const prerender = true;
|
|
5
|
+
|
|
6
|
+
function toRfc822(dateStr: string): string {
|
|
7
|
+
if (!dateStr) return new Date().toUTCString();
|
|
8
|
+
return new Date(dateStr).toUTCString();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function escapeXml(str: string): string {
|
|
12
|
+
return str
|
|
13
|
+
.replace(/&/g, '&')
|
|
14
|
+
.replace(/</g, '<')
|
|
15
|
+
.replace(/>/g, '>')
|
|
16
|
+
.replace(/"/g, '"');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function GET(): Response {
|
|
20
|
+
const siteConfig = loadSiteConfig(config);
|
|
21
|
+
const base = siteConfig.url.replace(/\/$/, '');
|
|
22
|
+
const articles = getArticles(config);
|
|
23
|
+
|
|
24
|
+
const items = articles
|
|
25
|
+
.map((a) => {
|
|
26
|
+
const url = `${base}/articles/${a.slug}`;
|
|
27
|
+
return ` <item>
|
|
28
|
+
<title>${escapeXml(a.title)}</title>
|
|
29
|
+
<link>${url}</link>
|
|
30
|
+
<description>${escapeXml(a.excerpt)}</description>
|
|
31
|
+
<pubDate>${toRfc822(a.date)}</pubDate>
|
|
32
|
+
<guid isPermaLink="true">${url}</guid>
|
|
33
|
+
</item>`;
|
|
34
|
+
})
|
|
35
|
+
.join('\n');
|
|
36
|
+
|
|
37
|
+
const rss = `<?xml version="1.0" encoding="UTF-8"?>
|
|
38
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
39
|
+
<channel>
|
|
40
|
+
<title>${escapeXml(siteConfig.name)}</title>
|
|
41
|
+
<link>${base}</link>
|
|
42
|
+
<description>${escapeXml(siteConfig.description)}</description>
|
|
43
|
+
<language>en</language>
|
|
44
|
+
<atom:link href="${base}/rss.xml" rel="self" type="application/rss+xml" />
|
|
45
|
+
${items}
|
|
46
|
+
</channel>
|
|
47
|
+
</rss>`;
|
|
48
|
+
|
|
49
|
+
return new Response(rss, {
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/rss+xml; charset=utf-8',
|
|
52
|
+
'Cache-Control': 'max-age=0, s-maxage=3600',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getSlugs, getTags } from 'cosmolo';
|
|
2
|
+
import { getCategorySlugs, loadSiteConfig } from 'cosmolo';
|
|
3
|
+
import { getPageSlugs } from 'cosmolo';
|
|
4
|
+
import config from '../../../cosmolo.config';
|
|
5
|
+
|
|
6
|
+
export const prerender = true;
|
|
7
|
+
|
|
8
|
+
export function GET(): Response {
|
|
9
|
+
const siteConfig = loadSiteConfig(config);
|
|
10
|
+
const base = siteConfig.url.replace(/\/$/, '');
|
|
11
|
+
|
|
12
|
+
const articleUrls = getSlugs(config).map((slug) => `${base}/articles/${slug}`);
|
|
13
|
+
const categoryUrls = getCategorySlugs(config).map((slug) => `${base}/categories/${slug}`);
|
|
14
|
+
const pageUrls = getPageSlugs(config).map((slug) => `${base}/${slug}`);
|
|
15
|
+
const tagUrls = getTags(config).map((tag) => `${base}/tags/${tag}`);
|
|
16
|
+
|
|
17
|
+
const allUrls = [base, ...articleUrls, ...categoryUrls, ...pageUrls, ...tagUrls];
|
|
18
|
+
|
|
19
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
20
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
21
|
+
${allUrls.map((url) => ` <url><loc>${url}</loc></url>`).join('\n')}
|
|
22
|
+
</urlset>`;
|
|
23
|
+
|
|
24
|
+
return new Response(sitemap, {
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/xml; charset=utf-8',
|
|
27
|
+
'Cache-Control': 'max-age=0, s-maxage=3600',
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { sveltekit } from '@sveltejs/kit/vite';
|
|
2
|
+
import { cosmoloPlugin } from 'cosmolo/plugin';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
import config from './cosmolo.config';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [sveltekit(), cosmoloPlugin(config)],
|
|
8
|
+
});
|