@voltro/cli 0.52.0 → 0.53.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/CHANGELOG.md +229 -0
- package/THIRD-PARTY-NOTICES.md +8311 -3318
- package/dist/{apiBuild-CSFI8QGq.js → apiBuild-CaPfoWku.js} +11 -5
- package/dist/apiBuild-DHtLXYx9.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/build-D-OnvNMf.js +843 -0
- package/dist/{checkCommand-COmqc2cB.js → checkCommand-C5elt0tW.js} +6 -6
- package/dist/{checkCommand-2SbqzukH.js → checkCommand-D2ZduVlh.js} +1 -1
- package/dist/codegen-BWpt3VgF.js +2 -0
- package/dist/{codegen-VF479Cnb.js → codegen-FEk8AZHb.js} +2 -2
- package/dist/{codegenCommand-DCdG2JN-.js → codegenCommand-BOiWQ5hz.js} +4 -4
- package/dist/{codemodRunner-r7J9lIa7.js → codemodRunner-BjtB2lq6.js} +109 -11
- package/dist/{commands-Cc_nV8WI.js → commands-DyxAmhP0.js} +30 -30
- package/dist/{dashboardCommand-C-gKvwqh.js → dashboardCommand-BdKTyT13.js} +5 -5
- package/dist/{dataCommand-BgpBHnlB.js → dataCommand-Bab9X7s8.js} +2 -2
- package/dist/{dbCommand-sHedr-NJ.js → dbCommand-06O2finM.js} +277 -236
- package/dist/dbCommand-B1EXBC6f.js +2 -0
- package/dist/{dev-CRHoCEiy.js → dev-C6LGF4iY.js} +3006 -2426
- package/dist/{dev--A3nsxA3.js → dev-GjJWAYo2.js} +1 -1
- package/dist/doctorCommand-B0hX0tdz.js +2 -0
- package/dist/{doctorCommand-DtfJ3FA6.js → doctorCommand-etMkflRc.js} +101 -69
- package/dist/{dormancyCommand-Drn7o0No.js → dormancyCommand-UwZ1AZzB.js} +1 -1
- package/dist/{embeddingsCommand-Z-jO1fWN.js → embeddingsCommand-C70zWHwo.js} +1 -1
- package/dist/{envCommand-D4gCrrTZ.js → envCommand-dSyKvRkM.js} +18 -18
- package/dist/{evolveCommand-CMROeKeA.js → evolveCommand-CG0_ebO5.js} +2 -2
- package/dist/fileConventions-DASGEmj-.js +35 -0
- package/dist/{fileTaxonomy-DvDUV9wq.js → fileTaxonomy-B7uxipWS.js} +55 -55
- package/dist/fontPipeline-LxIHa1vo.js +2 -0
- package/dist/fontPipeline-Tsh8kZfA.js +152 -0
- package/dist/frameworkTableAssembly-C_7Z-rMs.js +2 -0
- package/dist/{frameworkTableAssembly-w-XnLa3q.js → frameworkTableAssembly-DKx3ba3S.js} +5 -5
- package/dist/imagePipeline-B_GVJgm6.js +2 -0
- package/dist/imagePipeline-CBZmjT4i.js +127 -0
- package/dist/index.js +1 -1
- package/dist/{infoCommand-DXM868o_.js → infoCommand-_53iOc_j.js} +1 -1
- package/dist/{inspect-CtL_xTbu.js → inspect-Bd8-9wsi.js} +1 -1
- package/dist/inspect-CuoDInfZ.js +2 -0
- package/dist/{inspectGateHint-BjnFubmH.js → inspectGateHint-4LxkNtrz.js} +1 -1
- package/dist/manifestBuild-C4-J1-m_.js +2 -0
- package/dist/{manifestBuild-CuU1VrSm.js → manifestBuild-Cqgsx2bM.js} +1 -1
- package/dist/{metaCommands-C6RFmF1r.js → metaCommands-Cn2oboG4.js} +8 -2
- package/dist/{migrate-D0F-eTlK.js → migrate-Cko9rswM.js} +2 -2
- package/dist/{pageConvention-CzUiSbtU.js → pageConvention-C938S8oC.js} +1 -1
- package/dist/{privacyCommand-DGdopOI6.js → privacyCommand-DWTQMC6R.js} +2 -2
- package/dist/{probeCommand-C9gazU0H.js → probeCommand-DkGGLknv.js} +83 -24
- package/dist/{renderModeScan-CcH2X1_D.js → renderModeScan-CUbOeOAg.js} +3 -1
- package/dist/{renderProfile-Ck32Fzxr.js → renderProfile-CskIgAfn.js} +2 -2
- package/dist/{runtimeTrace-BPQyCmC5.js → runtimeTrace-c0APJz7E.js} +1 -1
- package/dist/{sdkgen-Se88ifTd.js → sdkgen-BiQCgIEr.js} +1 -1
- package/dist/{serveCommand-DkP3OT0W.js → serveCommand-CueKQgzl.js} +869 -805
- package/dist/serveCommand-DsnrVN3U.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/start-BJzZLbt8.js +3 -0
- package/dist/start-ekPan8BT.js +1510 -0
- package/dist/startEntry.js +1 -1
- package/dist/{staticCommand-BwNEDlSU.js → staticCommand-xlSL-IWk.js} +1 -1
- package/dist/{test-f3amja6a.js → test-BWPQcRoB.js} +1 -1
- package/dist/updateCommand-Bqql_rsQ.js +2 -0
- package/dist/{updateCommand-BMk2e4ky.js → updateCommand-C_8I8Rzo.js} +139 -115
- package/dist/webDev-C7jWJ5dX.js +2 -0
- package/dist/{webDev-BgWL9gKV.js → webDev-oczpugbx.js} +1455 -922
- package/dist/{webhooksCommand-CoIO3jbj.js → webhooksCommand-4SVPDjKg.js} +1 -1
- package/package.json +60 -19
- package/templates/AGENTS.md +5 -3
- package/templates/agent-docs/_index.md +5 -3
- package/templates/agent-docs/_manifest.json +20 -4
- package/templates/agent-docs/ai.md +2 -2
- package/templates/agent-docs/authentication.md +1 -1
- package/templates/agent-docs/cli.md +2 -2
- package/templates/agent-docs/configuration.md +17 -0
- package/templates/agent-docs/data.md +470 -26
- package/templates/agent-docs/database/advancedqueries.md +7 -7
- package/templates/agent-docs/database/columntypes.md +2 -2
- package/templates/agent-docs/database/querying.md +1 -1
- package/templates/agent-docs/database/schema.md +1 -1
- package/templates/agent-docs/database/seedsdialects.md +1 -1
- package/templates/agent-docs/database/transactions.md +3 -3
- package/templates/agent-docs/deployment.md +8 -0
- package/templates/agent-docs/internationalization.md +2 -2
- package/templates/agent-docs/introduction.md +7 -1
- package/templates/agent-docs/local-first-mobile.md +102 -4
- package/templates/agent-docs/observability.md +2 -0
- package/templates/agent-docs/plugins/auth.md +1 -1
- package/templates/agent-docs/plugins/comments.md +142 -0
- package/templates/agent-docs/plugins/notifications.md +47 -4
- package/templates/agent-docs/plugins/presence.md +16 -3
- package/templates/agent-docs/plugins/prometheus.md +1 -1
- package/templates/agent-docs/plugins/queue.md +129 -0
- package/templates/agent-docs/plugins.md +11 -7
- package/templates/agent-docs/reference.md +16 -4
- package/templates/agent-docs/routing.md +527 -3
- package/templates/agent-docs/schema-driven-ui.md +214 -3
- package/templates/agent-docs/security.md +3 -3
- package/templates/agent-docs/whats-new.md +160 -66
- package/templates/apps/api-ai/package.json +6 -6
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -9
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-row-history/package.json +8 -8
- package/templates/apps/api-saas/package.json +10 -10
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/app.config.ts +26 -2
- package/templates/apps/changelog/content/releases/{0.1.0.mdx → v0-1-0.mdx} +0 -1
- package/templates/apps/changelog/content/releases/{0.2.0.mdx → v0-2-0.mdx} +0 -1
- package/templates/apps/changelog/package.json +8 -8
- package/templates/apps/changelog/src/collections/releases.collection.ts +48 -0
- package/templates/apps/changelog/src/globals.d.ts +1 -1
- package/templates/apps/changelog/src/locales/de.ts +1 -1
- package/templates/apps/changelog/src/locales/en.ts +1 -1
- package/templates/apps/changelog/src/pages/[locale]/[slug]/page.tsx +7 -4
- package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +13 -41
- package/templates/apps/changelog/src/pages/[slug]/page.test.tsx +26 -51
- package/templates/apps/changelog/src/pages/[slug]/page.tsx +21 -19
- package/templates/apps/changelog/src/pages/page.test.tsx +14 -37
- package/templates/apps/changelog/src/pages/page.tsx +18 -12
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/content/docs/de/guides/first-page.md +4 -0
- package/templates/apps/frontend-docs/content/docs/de/intro/getting-started.md +4 -0
- package/templates/apps/frontend-docs/content/docs/en/guides/first-page.md +4 -0
- package/templates/apps/frontend-docs/content/docs/en/intro/getting-started.md +4 -0
- package/templates/apps/frontend-docs/package.json +8 -6
- package/templates/apps/frontend-docs/src/collections/docs.collection.ts +21 -0
- package/templates/apps/frontend-docs/src/locales/de.ts +0 -5
- package/templates/apps/frontend-docs/src/locales/en.ts +0 -5
- package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug]/page.tsx +17 -9
- package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +10 -5
- package/templates/apps/frontend-docs/src/pages/[locale]/page.tsx +8 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.test.tsx +53 -15
- package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.tsx +27 -33
- package/templates/apps/frontend-docs/src/pages/page.test.tsx +17 -3
- package/templates/apps/frontend-docs/src/pages/page.tsx +16 -12
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +6 -6
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/content/posts/cms-to-ssg.md +12 -0
- package/templates/apps/frontend-static-blog/content/posts/hello-static.md +12 -0
- package/templates/apps/frontend-static-blog/content/posts/islands-not-hydration.md +14 -0
- package/templates/apps/frontend-static-blog/package.json +8 -6
- package/templates/apps/frontend-static-blog/src/collections/posts.collection.ts +27 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug]/page.tsx +7 -4
- package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +7 -1
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.test.tsx +19 -9
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.tsx +19 -21
- package/templates/apps/frontend-static-blog/src/pages/page.test.tsx +18 -5
- package/templates/apps/frontend-static-blog/src/pages/page.tsx +21 -14
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-BYBpL7Pz.js +0 -2
- package/dist/build-CPgcMQug.js +0 -793
- package/dist/codegen-CctkDO-1.js +0 -2
- package/dist/dbCommand-DNb6yeOG.js +0 -2
- package/dist/doctorCommand-CqoWA2p5.js +0 -2
- package/dist/fileConventions-DOqD3lPS.js +0 -34
- package/dist/frameworkTableAssembly-C6ETawPR.js +0 -2
- package/dist/inspect-CuGDYES0.js +0 -2
- package/dist/manifestBuild-CPjhvM62.js +0 -2
- package/dist/serveCommand-DLc-BznW.js +0 -2
- package/dist/start-DfL3fOiN.js +0 -3
- package/dist/start-jw89Xbqy.js +0 -1339
- package/dist/updateCommand-5gFVfK5q.js +0 -2
- package/dist/webDev-CZbTsDcH.js +0 -2
- package/templates/apps/changelog/scripts/generate-rss.mjs +0 -38
- package/templates/apps/changelog/src/lib/releases.ts +0 -21
- package/templates/apps/frontend-static-blog/src/content/posts.ts +0 -64
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// A dynamic route — `/blog/<slug>` — pre-rendered STATICALLY, one HTML
|
|
2
2
|
// file per post. Three exports make that work:
|
|
3
3
|
//
|
|
4
|
-
// • getStaticPaths → enumerate which slugs to build (from the
|
|
5
|
-
//
|
|
6
|
-
// • loader → fetch THIS post's
|
|
4
|
+
// • getStaticPaths → enumerate which slugs to build (from the `posts`
|
|
5
|
+
// content collection — one .md file per post).
|
|
6
|
+
// • loader → fetch THIS post's entry; runs at build per slug and
|
|
7
|
+
// returns the RENDERED html (code fences highlighted
|
|
8
|
+
// server-side; nothing markdown-shaped in the bundle).
|
|
7
9
|
// • meta(loaderData)→ per-post <title>/<description> baked into the HTML.
|
|
8
10
|
//
|
|
9
11
|
// `interactive: 'islands'` hydrates ONLY the <ReadingProgress> island; the
|
|
@@ -11,8 +13,9 @@
|
|
|
11
13
|
|
|
12
14
|
import type { ReactNode } from 'react'
|
|
13
15
|
import { useLoaderData, notFound, type LoaderFn, type PageMeta } from '@voltro/web'
|
|
16
|
+
import { getCollection, getEntry, type ContentEntry } from '@voltro/content'
|
|
14
17
|
import { T, useLocale } from '@voltro/i18n'
|
|
15
|
-
import { posts, type Post } from '../../../
|
|
18
|
+
import { posts, type Post } from '../../../collections/posts.collection'
|
|
16
19
|
import { withLocalePrefix } from '../../../lib/locale'
|
|
17
20
|
import ReadingProgress from '../../../components/ReadingProgress.island'
|
|
18
21
|
|
|
@@ -22,14 +25,13 @@ export const interactive = 'islands' as const
|
|
|
22
25
|
// Which concrete paths to pre-render. Every returned `params` becomes one
|
|
23
26
|
// built HTML file; un-enumerated slugs are simply not built (→ 404).
|
|
24
27
|
export const getStaticPaths = async (): Promise<Array<{ params: { slug: string } }>> =>
|
|
25
|
-
posts.map((post) => ({ params: { slug: post.slug } }))
|
|
28
|
+
(await getCollection(posts.name)).map((post) => ({ params: { slug: post.slug } }))
|
|
26
29
|
|
|
27
30
|
// Runs at build time for each enumerated slug. Returning `notFound()` skips
|
|
28
|
-
// the artifact at build / 404s at runtime
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!post) return notFound(`post ${params.slug}`)
|
|
31
|
+
// the artifact at build / 404s at runtime.
|
|
32
|
+
export const loader: LoaderFn<ContentEntry<Post>> = async ({ params }) => {
|
|
33
|
+
const post = await getEntry<Post>(posts.name, params.slug)
|
|
34
|
+
if (post === null) return notFound(`post ${params.slug}`)
|
|
33
35
|
return post
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -37,15 +39,13 @@ export const loader: LoaderFn<Post> = async ({ params }) => {
|
|
|
37
39
|
// pre-rendered HTML (great for SEO + social cards). The title/description are
|
|
38
40
|
// POST CONTENT, not chrome, so they are the same on `/blog/x` and `/de/blog/x`
|
|
39
41
|
// — no per-locale variant needed here.
|
|
40
|
-
export const meta = ({ loaderData }: { loaderData: Post }): PageMeta => ({
|
|
41
|
-
title: `${loaderData.title} — {{capProjectName}}`,
|
|
42
|
-
description: loaderData.excerpt,
|
|
42
|
+
export const meta = ({ loaderData }: { loaderData: ContentEntry<Post> }): PageMeta => ({
|
|
43
|
+
title: `${loaderData.data.title} — {{capProjectName}}`,
|
|
44
|
+
description: loaderData.data.excerpt,
|
|
43
45
|
})
|
|
44
46
|
|
|
45
47
|
export default function BlogPost(): ReactNode {
|
|
46
|
-
const post = useLoaderData<Post
|
|
47
|
-
// The active locale (from the layout's URL provider) keeps the "all posts"
|
|
48
|
-
// link on the current language.
|
|
48
|
+
const post = useLoaderData<ContentEntry<Post>>()
|
|
49
49
|
const locale = useLocale()
|
|
50
50
|
return (
|
|
51
51
|
<main>
|
|
@@ -56,13 +56,11 @@ export default function BlogPost(): ReactNode {
|
|
|
56
56
|
</a>
|
|
57
57
|
</p>
|
|
58
58
|
<article>
|
|
59
|
-
<h1>{post.title}</h1>
|
|
59
|
+
<h1>{post.data.title}</h1>
|
|
60
60
|
<p className="muted">
|
|
61
|
-
{post.date} · <T id="post.readingTimeRead" values={{ minutes: post.readingMinutes }} />
|
|
61
|
+
{post.data.date} · <T id="post.readingTimeRead" values={{ minutes: post.data.readingMinutes }} />
|
|
62
62
|
</p>
|
|
63
|
-
{post.
|
|
64
|
-
<p key={i}>{para}</p>
|
|
65
|
-
))}
|
|
63
|
+
<div dangerouslySetInnerHTML={{ __html: post.html ?? '' }} />
|
|
66
64
|
</article>
|
|
67
65
|
</main>
|
|
68
66
|
)
|
|
@@ -12,10 +12,16 @@
|
|
|
12
12
|
import { afterEach, describe, expect, test, vi } from 'vitest'
|
|
13
13
|
import { act, createElement, type ReactNode } from 'react'
|
|
14
14
|
import { createRoot, type Root } from 'react-dom/client'
|
|
15
|
-
import {
|
|
15
|
+
import { dirname, join } from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
import { setContentRoot } from '@voltro/content'
|
|
16
18
|
import en from '../locales/en'
|
|
17
19
|
import de from '../locales/de'
|
|
18
20
|
|
|
21
|
+
// The list now comes from the posts CONTENT COLLECTION — point the server
|
|
22
|
+
// half at this app's root and load the REAL content/posts/*.md files.
|
|
23
|
+
setContentRoot(join(dirname(fileURLToPath(import.meta.url)), '..', '..'))
|
|
24
|
+
|
|
19
25
|
const catalogs: Record<string, Record<string, string>> = { en, de }
|
|
20
26
|
let currentLocale = 'en'
|
|
21
27
|
|
|
@@ -23,6 +29,10 @@ let currentLocale = 'en'
|
|
|
23
29
|
const fill = (msg: string, values?: Record<string, unknown>): string =>
|
|
24
30
|
values ? msg.replace(/\{(\w+)\}/g, (_, k: string) => String(values[k] ?? `{${k}}`)) : msg
|
|
25
31
|
|
|
32
|
+
vi.mock('@voltro/web', () => ({
|
|
33
|
+
useLoaderData: () => entries,
|
|
34
|
+
}))
|
|
35
|
+
|
|
26
36
|
vi.mock('@voltro/i18n', () => ({
|
|
27
37
|
// The locale catalogs call these at module top-level; identity mirrors the
|
|
28
38
|
// real (compile-time-only) implementations so importing them doesn't throw.
|
|
@@ -38,7 +48,9 @@ vi.mock('@voltro/i18n', () => ({
|
|
|
38
48
|
fill(catalogs[currentLocale]?.[id] ?? id, values),
|
|
39
49
|
}))
|
|
40
50
|
|
|
41
|
-
const { default: Index, meta, renderMode, interactive } = await import('./page')
|
|
51
|
+
const { default: Index, meta, renderMode, interactive, loader } = await import('./page')
|
|
52
|
+
// The real markdown entries, loaded through the real collection pipeline.
|
|
53
|
+
const entries = await loader()
|
|
42
54
|
|
|
43
55
|
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
|
|
44
56
|
|
|
@@ -83,11 +95,12 @@ describe('index — localized render', () => {
|
|
|
83
95
|
currentLocale = 'en'
|
|
84
96
|
render(createElement(Index))
|
|
85
97
|
const links = Array.from(container.querySelectorAll('.post-list a'))
|
|
86
|
-
expect(links).toHaveLength(
|
|
98
|
+
expect(links).toHaveLength(entries.length)
|
|
99
|
+
expect(entries.length).toBeGreaterThan(0)
|
|
87
100
|
// Newest-first: the 2025-02-03 post precedes the 2025-01-15 one.
|
|
88
101
|
expect(links[0]!.getAttribute('href')).toBe('/blog/cms-to-ssg')
|
|
89
102
|
expect(links[links.length - 1]!.getAttribute('href')).toBe('/blog/hello-static')
|
|
90
|
-
expect(container.textContent).toContain(`${
|
|
103
|
+
expect(container.textContent).toContain(`${entries.length} posts`)
|
|
91
104
|
})
|
|
92
105
|
|
|
93
106
|
test('prefixes post links with /de and renders German chrome (de)', () => {
|
|
@@ -95,6 +108,6 @@ describe('index — localized render', () => {
|
|
|
95
108
|
render(createElement(Index))
|
|
96
109
|
const links = Array.from(container.querySelectorAll('.post-list a'))
|
|
97
110
|
expect(links[0]!.getAttribute('href')).toBe('/de/blog/cms-to-ssg')
|
|
98
|
-
expect(container.textContent).toContain(`${
|
|
111
|
+
expect(container.textContent).toContain(`${entries.length} Beiträge`)
|
|
99
112
|
})
|
|
100
113
|
})
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
// Blog index, mounted at `/` (default locale) and mirrored at `/de` by
|
|
2
|
-
// src/pages/[locale]/
|
|
2
|
+
// src/pages/[locale]/page.tsx. The post LIST is pure content — no
|
|
3
3
|
// interactivity — so it ships zero JavaScript: `interactive = 'none'` strips
|
|
4
|
-
// every framework <script> from the rendered HTML.
|
|
5
|
-
//
|
|
4
|
+
// every framework <script> from the rendered HTML.
|
|
5
|
+
//
|
|
6
|
+
// The list comes from the `posts` CONTENT COLLECTION (content/posts/*.md):
|
|
7
|
+
// the loader runs at build time and reads the filesystem — markdown with
|
|
8
|
+
// schema-checked frontmatter, no markdown dependency in this app.
|
|
6
9
|
//
|
|
7
10
|
// `meta` is a FUNCTION of `{ locale }` — that's what localises each variant's
|
|
8
11
|
// <title> / <description> in the pre-rendered HTML. Post titles/excerpts are
|
|
9
|
-
// CONTENT
|
|
10
|
-
//
|
|
12
|
+
// CONTENT and are NOT translated; only the chrome (heading, count line,
|
|
13
|
+
// reading-time label) comes from the catalogs.
|
|
11
14
|
|
|
12
15
|
import type { ReactNode } from 'react'
|
|
13
|
-
import type
|
|
16
|
+
import { useLoaderData, type PageMeta } from '@voltro/web'
|
|
17
|
+
import { getCollection, type ContentEntry } from '@voltro/content'
|
|
14
18
|
import { T, useLocale } from '@voltro/i18n'
|
|
15
|
-
import { posts } from '../
|
|
19
|
+
import { posts, type Post } from '../collections/posts.collection'
|
|
16
20
|
import { getCatalog, withLocalePrefix } from '../lib/locale'
|
|
17
21
|
|
|
18
22
|
export const renderMode = 'static' as const
|
|
@@ -26,10 +30,13 @@ export const meta = ({ locale }: { locale: string }): PageMeta => {
|
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
// Newest first
|
|
30
|
-
const
|
|
33
|
+
// Newest first. Runs once at build time (this page is static).
|
|
34
|
+
export const loader = async (): Promise<ReadonlyArray<ContentEntry<Post>>> =>
|
|
35
|
+
[...await getCollection<Post>(posts.name)]
|
|
36
|
+
.sort((a, b) => b.data.date.localeCompare(a.data.date))
|
|
31
37
|
|
|
32
38
|
export default function Index(): ReactNode {
|
|
39
|
+
const entries = useLoaderData<ReadonlyArray<ContentEntry<Post>>>()
|
|
33
40
|
const locale = useLocale()
|
|
34
41
|
return (
|
|
35
42
|
<main>
|
|
@@ -37,18 +44,18 @@ export default function Index(): ReactNode {
|
|
|
37
44
|
<T id="home.heading" />
|
|
38
45
|
</h1>
|
|
39
46
|
<p className="muted">
|
|
40
|
-
<T id="home.postCount" values={{ count:
|
|
47
|
+
<T id="home.postCount" values={{ count: entries.length }} />
|
|
41
48
|
</p>
|
|
42
49
|
<ul className="post-list">
|
|
43
|
-
{
|
|
50
|
+
{entries.map((post) => (
|
|
44
51
|
<li key={post.slug}>
|
|
45
|
-
<a href={withLocalePrefix(`/blog/${post.slug}`, locale)}>{post.title}</a>
|
|
52
|
+
<a href={withLocalePrefix(`/blog/${post.slug}`, locale)}>{post.data.title}</a>
|
|
46
53
|
<p>
|
|
47
54
|
<span className="muted">
|
|
48
|
-
{post.date} · <T id="post.readingTime" values={{ minutes: post.readingMinutes }} />
|
|
55
|
+
{post.data.date} · <T id="post.readingTime" values={{ minutes: post.data.readingMinutes }} />
|
|
49
56
|
</span>
|
|
50
57
|
{' — '}
|
|
51
|
-
{post.excerpt}
|
|
58
|
+
{post.data.excerpt}
|
|
52
59
|
</p>
|
|
53
60
|
</li>
|
|
54
61
|
))}
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.53.0",
|
|
16
|
+
"@voltro/cli": "0.53.0",
|
|
17
|
+
"@voltro/client": "0.53.0",
|
|
18
|
+
"@voltro/env": "0.53.0",
|
|
19
|
+
"@voltro/i18n": "0.53.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.53.0",
|
|
21
|
+
"@voltro/web": "0.53.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.53.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"jsdom": "^30.0.0",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@react-native-async-storage/async-storage": "1.23.1",
|
|
17
17
|
"@react-native-community/netinfo": "11.4.1",
|
|
18
|
-
"@voltro/client": "0.
|
|
19
|
-
"@voltro/react-native": "0.
|
|
20
|
-
"@{{projectName}}/api": "0.
|
|
18
|
+
"@voltro/client": "0.53.0",
|
|
19
|
+
"@voltro/react-native": "0.53.0",
|
|
20
|
+
"@{{projectName}}/api": "0.53.0",
|
|
21
21
|
"expo": "^52.0.0",
|
|
22
22
|
"expo-constants": "~17.0.0",
|
|
23
23
|
"expo-linking": "~7.0.0",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@types/node": "^22.0.0",
|
|
33
33
|
"@types/react": "^19.0.0",
|
|
34
34
|
"@vitest/coverage-v8": "^4.1.10",
|
|
35
|
-
"@voltro/cli": "0.
|
|
35
|
+
"@voltro/cli": "0.53.0",
|
|
36
36
|
"typescript": "^6.0.3",
|
|
37
37
|
"vitest": "^4.1.10"
|
|
38
38
|
}
|