HelpLogin
© 2022-2025 Spice AI, Inc.
SQL Query ReferenceDocsFAQSupport
PrivacyTerms
Status
Home
Datasets
Models
trunk
Edit on GitHub
Fork
/docs/website/src/lib/articles.ts
1import yaml from 'yaml'
2import { remark } from 'remark'
3import remarkParse from 'remark-parse'
4import remarkFrontmatter from 'remark-frontmatter'
5import remarkExtractFrontmatter from 'remark-extract-frontmatter'
6
7import { GITHUB_BLOG_REPO } from './constants'
8
9type FrontMatter = {
10 date: string
11 title: string
12 type: string
13 author: string
14 categories: string[]
15 tags: string[]
16}
17
18export type ProcessedFile = FrontMatter & {
19 description: string
20 link: string
21}
22
23type GitHubContent = {
24 download_url: string
25 name: string
26}
27
28async function fetchContents(path: string): Promise<GitHubContent[]> {
29 try {
30 const response = await fetch(`${GITHUB_BLOG_REPO}/contents${path}`, { cache: 'force-cache' })
31
32 if (!response.ok) {
33 throw new Error('Failed to fetch releases')
34 }
35
36 const data = await response.json()
37 return data
38 } catch (error) {
39 console.error('Error fetching releases:', error)
40 return []
41 }
42}
43
44async function fetchPostContent(url: string) {
45 try {
46 const response = await fetch(url, { cache: 'force-cache' })
47 if (!response.ok) {
48 throw new Error(`Failed to fetch content from ${url}`)
49 }
50
51 const content = await response.text()
52 return content
53 } catch (error) {
54 console.error('Error fetching post content:', error)
55 return null
56 }
57}
58
59function generateBlogLink(date: string, title: string) {
60 const formattedDate = date.replace(/-/g, '/')
61 const formattedTitle = title.toLowerCase().replace(/\s+/g, '-').replace(/[(),]/g, '')
62
63 return `https://blog.spiceai.org/posts/${formattedDate}/${formattedTitle}`
64}
65
66export async function getArticlesData(): Promise<ProcessedFile[]> {
67 const releases = await fetchContents('/website/blog/releases')
68
69 const processedReleases = []
70
71 for (const release of releases) {
72 try {
73 const content = await fetchPostContent(release.download_url)
74
75 if (!content) {
76 continue
77 }
78
79 const result = await remark()
80 .use(remarkParse)
81 .use(remarkFrontmatter, ['yaml'])
82 .use(remarkExtractFrontmatter, { yaml: yaml.parse })
83 .process(content)
84
85 const frontmatter = result.data as FrontMatter
86 const markdownContent = String(result)
87
88 // Extract content between '---\n' and '## Highlights'
89 const descriptionPiece = markdownContent.match(/---\n([\s\S]*?)\n## Highlights/)
90 const description = descriptionPiece ? descriptionPiece[1].trim() : ''
91
92 // Extract the last part of the content after the second '---\n'
93 const regex = /---\n([\s\S]*)/
94 const lastMessageDescription = description.match(regex)
95 const finalContent = lastMessageDescription ? lastMessageDescription[1].trim() : ''
96
97 processedReleases.push({
98 ...frontmatter,
99 description: finalContent,
100 link: generateBlogLink(frontmatter.date, frontmatter.title)
101 })
102 } catch (error) {
103 console.error('Error processing post:', release.name, error)
104 }
105 }
106 const sortedReleases = processedReleases.sort((a, b) => {
107 return new Date(b.date).getTime() - new Date(a.date).getTime()
108 })
109
110 return sortedReleases.slice(0, 3)
111}
1import yaml from 'yaml'
2import { remark } from 'remark'
3import remarkParse from 'remark-parse'
4import remarkFrontmatter from 'remark-frontmatter'
5import remarkExtractFrontmatter from 'remark-extract-frontmatter'
6
7import { GITHUB_BLOG_REPO } from './constants'
8
9type FrontMatter = {
10 date: string
11 title: string
12 type: string
13 author: string
14 categories: string[]
15 tags: string[]
16}
17
18export type ProcessedFile = FrontMatter & {
19 description: string
20 link: string
21}
22
23type GitHubContent = {
24 download_url: string
25 name: string
26}
27
28async function fetchContents(path: string): Promise<GitHubContent[]> {
29 try {
30 const response = await fetch(`${GITHUB_BLOG_REPO}/contents${path}`, { cache: 'force-cache' })
31
32 if (!response.ok) {
33 throw new Error('Failed to fetch releases')
34 }
35
36 const data = await response.json()
37 return data
38 } catch (error) {
39 console.error('Error fetching releases:', error)
40 return []
41 }
42}
43
44async function fetchPostContent(url: string) {
45 try {
46 const response = await fetch(url, { cache: 'force-cache' })
47 if (!response.ok) {
48 throw new Error(`Failed to fetch content from ${url}`)
49 }
50
51 const content = await response.text()
52 return content
53 } catch (error) {
54 console.error('Error fetching post content:', error)
55 return null
56 }
57}
58
59function generateBlogLink(date: string, title: string) {
60 const formattedDate = date.replace(/-/g, '/')
61 const formattedTitle = title.toLowerCase().replace(/\s+/g, '-').replace(/[(),]/g, '')
62
63 return `https://blog.spiceai.org/posts/${formattedDate}/${formattedTitle}`
64}
65
66export async function getArticlesData(): Promise<ProcessedFile[]> {
67 const releases = await fetchContents('/website/blog/releases')
68
69 const processedReleases = []
70
71 for (const release of releases) {
72 try {
73 const content = await fetchPostContent(release.download_url)
74
75 if (!content) {
76 continue
77 }
78
79 const result = await remark()
80 .use(remarkParse)
81 .use(remarkFrontmatter, ['yaml'])
82 .use(remarkExtractFrontmatter, { yaml: yaml.parse })
83 .process(content)
84
85 const frontmatter = result.data as FrontMatter
86 const markdownContent = String(result)
87
88 // Extract content between '---\n' and '## Highlights'
89 const descriptionPiece = markdownContent.match(/---\n([\s\S]*?)\n## Highlights/)
90 const description = descriptionPiece ? descriptionPiece[1].trim() : ''
91
92 // Extract the last part of the content after the second '---\n'
93 const regex = /---\n([\s\S]*)/
94 const lastMessageDescription = description.match(regex)
95 const finalContent = lastMessageDescription ? lastMessageDescription[1].trim() : ''
96
97 processedReleases.push({
98 ...frontmatter,
99 description: finalContent,
100 link: generateBlogLink(frontmatter.date, frontmatter.title)
101 })
102 } catch (error) {
103 console.error('Error processing post:', release.name, error)
104 }
105 }
106 const sortedReleases = processedReleases.sort((a, b) => {
107 return new Date(b.date).getTime() - new Date(a.date).getTime()
108 })
109
110 return sortedReleases.slice(0, 3)
111}