@fullstackdatasolutions/articles 0.1.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.
Files changed (53) hide show
  1. package/README.md +324 -0
  2. package/dist/index.cjs +1027 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +232 -0
  5. package/dist/index.d.ts +232 -0
  6. package/dist/index.js +974 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/server.cjs +732 -0
  9. package/dist/server.cjs.map +1 -0
  10. package/dist/server.d.cts +127 -0
  11. package/dist/server.d.ts +127 -0
  12. package/dist/server.js +684 -0
  13. package/dist/server.js.map +1 -0
  14. package/package.json +82 -0
  15. package/src/ArticleCard.tsx +63 -0
  16. package/src/ArticleCategoryGrid.tsx +73 -0
  17. package/src/ArticleSchemas.tsx +82 -0
  18. package/src/ArticleSearchBar.tsx +50 -0
  19. package/src/ArticlesHero.tsx +33 -0
  20. package/src/ArticlesPage.tsx +167 -0
  21. package/src/CategoryArticlesPage.tsx +84 -0
  22. package/src/CommentForm.tsx +98 -0
  23. package/src/CommentItem.tsx +123 -0
  24. package/src/CommentThread.tsx +40 -0
  25. package/src/CommentsSection.tsx +84 -0
  26. package/src/FeaturedArticle.tsx +63 -0
  27. package/src/LatestArticles.tsx +42 -0
  28. package/src/LatestArticlesSection.tsx +68 -0
  29. package/src/__tests__/ArticleCard.test.tsx +78 -0
  30. package/src/__tests__/ArticleCategoryGrid.test.tsx +98 -0
  31. package/src/__tests__/ArticleSchemas.test.tsx +130 -0
  32. package/src/__tests__/ArticleSearchBar.test.tsx +79 -0
  33. package/src/__tests__/ArticlesHero.test.tsx +38 -0
  34. package/src/__tests__/ArticlesPage.test.tsx +155 -0
  35. package/src/__tests__/CategoryArticlesPage.test.tsx +156 -0
  36. package/src/__tests__/CommentForm.test.tsx +80 -0
  37. package/src/__tests__/CommentItem.test.tsx +149 -0
  38. package/src/__tests__/CommentsSection.test.tsx +118 -0
  39. package/src/__tests__/FeaturedArticle.test.tsx +85 -0
  40. package/src/__tests__/LatestArticles.test.tsx +157 -0
  41. package/src/__tests__/LatestArticlesSection.test.tsx +105 -0
  42. package/src/__tests__/jest-dom.d.ts +1 -0
  43. package/src/__tests__/seoUtils.test.ts +217 -0
  44. package/src/__tests__/useArticles.test.ts +207 -0
  45. package/src/articleTypes.ts +21 -0
  46. package/src/articlesConfig.ts +98 -0
  47. package/src/commentTypes.ts +14 -0
  48. package/src/index.ts +35 -0
  49. package/src/markdown.ts +314 -0
  50. package/src/seoUtils.ts +155 -0
  51. package/src/server-articles.ts +265 -0
  52. package/src/server.ts +25 -0
  53. package/src/useArticles.ts +93 -0
package/README.md ADDED
@@ -0,0 +1,324 @@
1
+ # @fullstackdatasolutions/articles
2
+
3
+ A self-contained Next.js articles library providing components, server utilities, SEO helpers, and a comments system. Drop it into any Next.js + Tailwind CSS v4 app.
4
+
5
+ ---
6
+
7
+ ## Quick start
8
+
9
+ ### 1. Install
10
+
11
+ ```bash
12
+ npm install @fullstackdatasolutions/articles
13
+ # or
14
+ pnpm add @fullstackdatasolutions/articles
15
+ ```
16
+
17
+ ### 2. Add the Tailwind source path
18
+
19
+ In Tailwind CSS v4, you add a `@source` directive to your global CSS file — **not** a `tailwind.config.ts`. This tells Tailwind to scan the package's source files so all utility classes are generated.
20
+
21
+ ```css
22
+ /* app/globals.css */
23
+ @import "tailwindcss";
24
+
25
+ @source "./node_modules/@fullstackdatasolutions/articles/src";
26
+ ```
27
+
28
+ The package components use Tailwind semantic tokens (`bg-card`, `text-muted-foreground`, `border-border`, etc.) that shadcn/ui and v0-generated apps already define — so the styling integrates automatically with your existing theme.
29
+
30
+ ### 3. Add TypeScript path aliases
31
+
32
+ If you're using TypeScript and want IDE autocomplete without a pre-built `dist/`, add these paths to `tsconfig.json`. This is **required in a monorepo** (where the package is a workspace dep) and optional when installed from npm (the published `dist/` files work without it).
33
+
34
+ ```json
35
+ // tsconfig.json
36
+ {
37
+ "compilerOptions": {
38
+ "paths": {
39
+ "@fullstackdatasolutions/articles": ["./node_modules/@fullstackdatasolutions/articles/src/index.ts"],
40
+ "@fullstackdatasolutions/articles/server": ["./node_modules/@fullstackdatasolutions/articles/src/server.ts"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### 4. Create your site config
47
+
48
+ ```ts
49
+ // config/articles.ts
50
+ import type { ArticlesConfig } from '@fullstackdatasolutions/articles'
51
+
52
+ export const siteConfig: ArticlesConfig = {
53
+ siteUrl: 'https://yoursite.com',
54
+ siteName: 'Your Site',
55
+ pageSize: 6,
56
+ }
57
+ ```
58
+
59
+ ### 5. Wire up the route files
60
+
61
+ ```tsx
62
+ // app/articles/page.tsx
63
+ import { ArticlesPage } from '@fullstackdatasolutions/articles'
64
+ import { siteConfig } from '@/config/articles'
65
+ export default function Page() {
66
+ return <ArticlesPage config={siteConfig} />
67
+ }
68
+
69
+ // app/articles/[slug]/page.tsx
70
+ import { ArticlePage, generateArticleStaticParams, generateArticleMetadata } from '@fullstackdatasolutions/articles/server'
71
+ import { siteConfig } from '@/config/articles'
72
+ export const generateStaticParams = () => generateArticleStaticParams()
73
+ export const generateMetadata = ({ params }: { params: { slug: string } }) =>
74
+ generateArticleMetadata(params.slug, siteConfig)
75
+ export default function Page({ params }: { params: { slug: string } }) {
76
+ return <ArticlePage slug={params.slug} config={siteConfig} />
77
+ }
78
+
79
+ // app/articles/category/[category]/page.tsx
80
+ import { CategoryArticlesPage, generateCategoryStaticParams, generateCategoryMetadata } from '@fullstackdatasolutions/articles/server'
81
+ import { siteConfig } from '@/config/articles'
82
+ export const generateStaticParams = () => generateCategoryStaticParams()
83
+ export const generateMetadata = ({ params }: { params: { category: string } }) =>
84
+ generateCategoryMetadata(params.category, siteConfig)
85
+ export default function Page({ params }: { params: { category: string } }) {
86
+ return <CategoryArticlesPage category={params.category} config={siteConfig} />
87
+ }
88
+ ```
89
+
90
+ ### 6. Copy the API routes
91
+
92
+ These cannot live inside the package — Next.js file-based routing requires them in your app. Copy these files from the reference implementation:
93
+
94
+ ```
95
+ app/api/articles/route.ts
96
+ app/api/articles/[slug]/comments/route.ts
97
+ app/api/articles/[slug]/comments/[commentId]/route.ts
98
+ ```
99
+
100
+ The route implementations import from `@fullstackdatasolutions/articles/server`. Do not put custom business logic in them; they should stay as thin wrappers.
101
+
102
+ ### 7. Add your content
103
+
104
+ Create `public/articles/[slug]/article.md` for each article. Required frontmatter:
105
+
106
+ ```markdown
107
+ ---
108
+ title: Your Article Title
109
+ excerpt: A one-sentence summary shown in cards and meta descriptions.
110
+ author: Jane Doe
111
+ tags: [campaigns, strategy]
112
+ ---
113
+
114
+ Article body in Markdown...
115
+ ```
116
+
117
+ ---
118
+
119
+ ## `ArticlesConfig` reference
120
+
121
+ | Field | Type | Default | Description |
122
+ |---|---|---|---|
123
+ | `siteUrl` | `string` | — | Canonical base URL. Used in metadata and JSON-LD. |
124
+ | `siteName` | `string` | — | Site name in title tags and JSON-LD. |
125
+ | `pageSize` | `number` | `6` | Articles per page / per "Load more" click. |
126
+ | `categoriesPageSize` | `number` | `8` | Category cards before "Load more categories". |
127
+ | `layout` | `ArticlesSection[]` | see below | Ordered sections to render. Omit a key to hide it. |
128
+ | `theme` | `ArticlesTheme` | — | CSS custom-property overrides for colors and fonts. |
129
+ | `categoryDescriptions` | `Record<string, string \| CategoryDescription>` | — | Short/long text per category slug. |
130
+ | `comments` | `CommentsConfig` | — | Comments feature config. Omit to disable entirely. |
131
+
132
+ **Default layout order:** `['hero', 'search', 'featured', 'latest', 'categories']`
133
+
134
+ To add a newsletter section, pass `layout: ['hero','search','featured','latest','categories','newsletter']` — the library renders `null` for `'newsletter'` so you can append your own component after `<ArticlesPage />`.
135
+
136
+ ---
137
+
138
+ ## Theming
139
+
140
+ All theme values are applied as CSS custom properties on a wrapper `<div>`. Components reference them through Tailwind so no extra CSS file is needed.
141
+
142
+ ```ts
143
+ theme: {
144
+ fontFamily: "'Inter', sans-serif",
145
+ headerColor: '#111827',
146
+ textColor: '#6b7280',
147
+ backgroundColor: '#ffffff',
148
+ linkColor: '#4f46e5',
149
+ linkHoverColor: '#4338ca',
150
+ borderRadius: '0.5rem',
151
+ }
152
+ ```
153
+
154
+ All fields are optional. Omitted fields fall back to your Tailwind theme tokens.
155
+
156
+ ---
157
+
158
+ ## Comments setup
159
+
160
+ ### 1. Enable in config
161
+
162
+ ```ts
163
+ export const siteConfig: ArticlesConfig = {
164
+ // ...
165
+ comments: {
166
+ enabled: true,
167
+ perArticleOverride: {
168
+ 'sensitive-article': false, // opt individual articles out
169
+ },
170
+ },
171
+ }
172
+ ```
173
+
174
+ ### 2. Copy the comment API routes
175
+
176
+ ```
177
+ app/api/articles/[slug]/comments/route.ts ← GET list, POST new comment
178
+ app/api/articles/[slug]/comments/[commentId]/route.ts ← DELETE (soft)
179
+ ```
180
+
181
+ ### 3. Requirements
182
+
183
+ - **NextAuth** session — users must be signed in to post. Unauthenticated visitors see a sign-in prompt.
184
+ - **PostgreSQL** — comments are stored via Prisma. Run the migration from the reference app's `backend/` directory.
185
+
186
+ ---
187
+
188
+ ## SEO utilities
189
+
190
+ ```ts
191
+ // app/articles/[slug]/page.tsx
192
+ import { generateArticleMetadata } from '@fullstackdatasolutions/articles/server'
193
+ export const generateMetadata = ({ params }) =>
194
+ generateArticleMetadata(params.slug, siteConfig)
195
+ // Produces: title, description, full OpenGraph, Twitter Card, canonical URL
196
+
197
+ // app/articles/category/[category]/page.tsx
198
+ import { generateCategoryMetadata } from '@fullstackdatasolutions/articles/server'
199
+ export const generateMetadata = ({ params }) =>
200
+ generateCategoryMetadata(params.category, siteConfig)
201
+
202
+ // app/sitemap.ts
203
+ import { getArticleSitemapEntries } from '@fullstackdatasolutions/articles/server'
204
+ export default async function sitemap() {
205
+ return getArticleSitemapEntries('https://yoursite.com')
206
+ // Returns both article entries (priority 0.8) and category entries (priority 0.7)
207
+ }
208
+ ```
209
+
210
+ JSON-LD structured data (`ArticleSchema`, `BreadcrumbSchema`, `CollectionPageSchema`) is rendered automatically inside the library components — no extra wiring required.
211
+
212
+ ---
213
+
214
+ ## Article frontmatter reference
215
+
216
+ | Field | Type | Required | Description |
217
+ |---|---|---|---|
218
+ | `title` | `string` | yes | Article title |
219
+ | `excerpt` | `string` | yes | One-sentence summary for cards and meta |
220
+ | `author` | `string` | no | Defaults to `'Andrew Blase'` |
221
+ | `tags` | `string[]` | no | Used as categories. First tag = primary category. |
222
+ | `date` | `YYYY-MM-DD` | no | Omit to publish immediately. Future dates hide until that date. |
223
+ | `featuredImage` | `string` | no | Filename relative to the article directory. Auto-detected if omitted. |
224
+
225
+ ---
226
+
227
+ ## Server utilities
228
+
229
+ Import from `@fullstackdatasolutions/articles/server` — these use `fs`/`path` and must never be imported in client components.
230
+
231
+ ```ts
232
+ import {
233
+ getAllArticles,
234
+ getArticleMetadata,
235
+ getArticlesByCategory,
236
+ getAllCategories,
237
+ getAvailableArticleSlugs,
238
+ getAdjacentArticles,
239
+ searchArticles,
240
+ categoryToSlug,
241
+ getArticleSitemapEntries,
242
+ generateArticleMetadata,
243
+ generateCategoryMetadata,
244
+ } from '@fullstackdatasolutions/articles/server'
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Using in a monorepo (workspace)
250
+
251
+ If the package is a `workspace:*` dependency in a pnpm/npm monorepo, point TypeScript directly at the source so no build step is needed during local development:
252
+
253
+ ```json
254
+ // tsconfig.json — consuming app
255
+ {
256
+ "compilerOptions": {
257
+ "paths": {
258
+ "@fullstackdatasolutions/articles": ["../packages/articles/src/index.ts"],
259
+ "@fullstackdatasolutions/articles/server": ["../packages/articles/src/server.ts"]
260
+ }
261
+ }
262
+ }
263
+ ```
264
+
265
+ And point the Tailwind `@source` at the workspace path:
266
+
267
+ ```css
268
+ /* app/globals.css */
269
+ @source "../../packages/articles/src";
270
+ ```
271
+
272
+ The relative path from your CSS file to `packages/articles/src` will vary by your monorepo layout.
273
+
274
+ ---
275
+
276
+ ## Adding to your app's `AGENTS.md`
277
+
278
+ Paste this block into your app's `AGENTS.md` so AI assistants know how the library works:
279
+
280
+ ```markdown
281
+ ## Articles library
282
+
283
+ This app uses `@fullstackdatasolutions/articles` for the articles section.
284
+
285
+ ### Key files
286
+ - `config/articles.ts` — all library behaviour (layout, theme, page size, comments) is controlled here
287
+ - `app/api/articles/` — thin API route wrappers; do not add business logic here
288
+ - `public/articles/[slug]/article.md` — one directory per article
289
+
290
+ ### CSS (Tailwind v4)
291
+ The package ships its source TypeScript files so Tailwind can scan them.
292
+ `app/globals.css` contains:
293
+ @source "./node_modules/@fullstackdatasolutions/articles/src";
294
+ If this line is missing, component classes (layout, images, line-clamp) will not render correctly.
295
+
296
+ ### Rules
297
+ - Change `config/articles.ts` first; only edit library source if config cannot express the need.
298
+ - When adding a new article: create `public/articles/[slug]/article.md` with title, excerpt, author, tags.
299
+ - API routes are copied from the library — update the package version and re-copy if the library API changes.
300
+ - Full library docs: `node_modules/@fullstackdatasolutions/articles/README.md`
301
+ ```
302
+
303
+ ---
304
+
305
+ ## Publishing
306
+
307
+ The package publishes to npm automatically when you push a tag:
308
+
309
+ ```bash
310
+ git tag articles-v0.2.0
311
+ git push origin articles-v0.2.0
312
+ ```
313
+
314
+ GitHub Actions runs typecheck → test → build → `npm publish --access public`. Requires an `NPM_TOKEN` secret in the repo settings.
315
+
316
+ ---
317
+
318
+ ## Upgrading
319
+
320
+ Follow semver. Breaking changes (major) include a migration note in the changelog.
321
+
322
+ - **Patch** — bug fixes only, safe to update
323
+ - **Minor** — new config options or exports, backwards-compatible
324
+ - **Major** — `ArticlesConfig` shape or component prop changes; check migration notes