chore: add initial changes to support new content

This commit is contained in:
Jonathan Summers-Muir
2022-06-03 15:09:23 +08:00
parent e90043d426
commit e26c1772ff
9 changed files with 721 additions and 1 deletions
@@ -0,0 +1,73 @@
---
title: Supabase vs Firebase
description: Supabase vs Firebase
author: ant_wilson
image: all-pull-together/all-pull-together-thumb.png
thumb: all-pull-together/all-pull-together-thumb.png
tags:
- community
- case-study
- q-and-a
date: '2022-05-26'
toc_depth: 3
---
## What is Firebase?
Now owned by Google, Firebase is a collection of tools aimed at mobile and web developers. At its core is the Firestore database.
Firestore allows you to store “documents”. These are collections of key:value pairs where the value can be another sub-document. Document based storage is perfect for unstructured data, since two documents in a collection do not necessarily need to have the same structure.
Firebase also offers other things that web developers find useful, like an auth service for user management, and wrappers for other Google services such as Cloud Functions, and File Storage.
## What is Supabase?
Supabase is an open source firebase alternative, but instead of being built around a document-based datastore, Supabase offers a relational database management system - called PostgreSQL. This comes with a few advantages:
- Its open source, so there is zero lock in.
- You can query it with SQL, a proven and powerful query language.
- It has a long track record of being used at scale.
- Its the database of choice for transactional workloads (think apps and websites, or other things that require near-instant responses to queries)
- It comes with decades of [useful postgres extensions and plug-ins](https://supabase.com/docs/guides/database/extensions)
At Supabase weve always been huge fans of Firebase - so we started adding a few things on top of PostgreSQL in an attempt to reach feature parity, including:
- Auto-generated API - [query your data straight from the client](https://supabase.com/docs/guides/api#rest-api-2)
- Realtime - [changes in your data will be streamed directly to your application](https://supabase.com/docs/reference/dart/subscribe)
- Auth - [a simple to integrate auth system and SQL based rules engine](https://supabase.com/auth)
- Functions - [javascript and typescript functions that deploy out globally](https://supabase.com/edge-functions)
- Storage - [hosting images, videos, and pdfs easily](https://supabase.com/storage)
## How are they similar?
Both Firebase and Supabase are based on the idea of bringing a superior developer experience to databases. With both platforms you can spin up a new project from directly inside the browser, without the need to download any extra tools or software to your machine. Both platforms come with a useful dashboard UI for debugging your data in realtime, which is especially useful for fast iterations when in development.
Both Firebase and Supabase have invested heavily in client side libraries so you can communicate with your database, directly from the client. Firebase has their [Firebase Javascript SDK](https://github.com/firebase/firebase-js-sdk) and Supabase has [supabase-js an isomorphic client](https://github.com/supabase/supabase-js/) that can be used both on the client also on the server in a node-js environment.
## How are they different?
Firebase and Supabase differ in several ways. The main one being that Firebase is a document store, whereas Supabase is based on PostgreSQL - a relational, SQL-based database management system. But there are also some other important differences.
### Open Source
Supabase is fully open source, meaning that along with the hosted cloud platform, you can also take the Supabase stack, and host it inside your own cloud or run it locally on your machine. The primary benefit here is no vendor lock in, which can be a frustrating problem for some Firebase users.
### Pricing
[Firebase charges for reads, writes and deletes](https://firebase.google.com/pricing), which can lead to some unpredictability, especially in the early stages of a project when your application is in heavy development. Supabase on the other hand [charges based on the amount of data stored](https://supabase.com/pricing), with breathing room for unlimited API requests and an unlimited number of Auth users.
### Scaling
One scaling limitation you might face when using Firestore - is the 1 document per second limitation on writes. It can be hard to architect around this issue for users who want to be able to sustain high write loads from a single client. Supabase is built on Postgres which is a well battle tested at scale, to help scale the number of connections, every deployment comes with an instance with PgBouncer, a [Postgres connection pooler perfect for use in Serverless computing](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pool) environments.
## How do I migrate from Firebase to Supabase?
Since Firebase is documents based, migrating into a relational database requires you to map your data structure across into a SQL schema. Luckily weve built a [handy conversion tool to do it for you](https://github.com/supabase-community/firebase-to-supabase/tree/main/firestore).
We also have a tool for [migrating Firebase Auth to Supabase Auth](https://github.com/supabase-community/firebase-to-supabase/tree/main/auth). And one for [migrating Firebase Storage files to Supabase Storage](https://github.com/supabase-community/firebase-to-supabase/tree/main/storage).
These are by far the most complete Firebase to Postgres migration tools available anywhere on the web.
If you require Enterprise level support with your project or migration, please get in touch using our [Enterprise contact form](https://supabase.com/contact/enterprise).
[Try Supabase for free today](https://app.supabase.io).
@@ -0,0 +1,77 @@
---
title: Supabase vs Heroku Postgres
description: Supabase vs Heroku Postgres
author: ant_wilson
image: all-pull-together/all-pull-together-thumb.png
thumb: all-pull-together/all-pull-together-thumb.png
tags:
- community
- case-study
- q-and-a
date: '2022-05-26'
toc_depth: 3
---
## What is Heroku Postgres?
Heroku is a cloud application platform that offers managed PostgreSQL as a service. They offer 5 levels of Postgres support from the Hobby Tier up to the Shield Tier, each with different levels of features and pricing.
## What is Supabase?
Supabase also offers managed Postgres, the main difference is that with each deployment you also get:
- Auto-generated API - [never write an API again](https://supabase.com/docs/guides/api#rest-api-2)
- Realtime - [subscribe to data changes via websockets](https://supabase.com/docs/reference/dart/subscribe)
- Auth - [users can log in and out of your application](https://supabase.com/auth)
- Functions - [deploy custom logic to the edge](https://supabase.com/edge-functions)
- Storage - [serve large files and folders](https://supabase.com/storage)
- PgBouncer - [connection pooling useful for serverless computing](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pool)
## How are they similar?
Heroku Postgres and Supabase both offer:
- [A web UI for managing your instance](https://supabase.com/docs/guides/database#table-view)
- SLAs and Enterprise-grade support packages
- Direct SSL connections to Postgres
- Postgres Extensions (see [Supabase Extensions](https://supabase.com/docs/guides/database/extensions), [Heroku Extensions](https://devcenter.heroku.com/articles/heroku-postgres-extensions-postgis-full-text-search))
- [Backups and PITR](https://supabase.com/blog/2020/08/02/continuous-postgresql-backup-walg) (not on Herokus Hobby tier)
- [Postgres logs](https://supabase.com/docs/guides/platform/logs) (not on Herokus Hobby tier)
- Encryption-at-rest (not on Herokus Hobby tier)
## What are the differences?
### Core Features
Both solutions run PostgreSQL, but in a time when Developer Experience matters, there is a lot you can do to improve the speed at which developers can build products faster and with less human resource. These are some of the key differences between Heroku Postgres and Supabase in terms of features:
- Supabase is more than just the raw database, it also comes with:
- [Connection pooling](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pool) so that you wont run out of connections in a serverless environment.
- [Auto-generated APIs](https://supabase.com/docs/guides/api#rest-api-2) based on your schema, so you can communicate with your database directly from the client.
- [Realtime API](https://supabase.com/docs/reference/dart/subscribe) is useful for when you want to subscribe to changes to your database over websockets.
- [Auth API](https://supabase.com/auth) can be used to leverage Postgress Row Level Security model, and control access to sensitive data on a per user, or per group level.
- [Functions](https://supabase.com/edge-functions) can be deployed out to the edge directly from the Supabase CLI, which means you can run sensitive business logic or transformations in a serverless fashion.
- [File Storage](https://supabase.com/storage) is useful for when your app needs to store large files and folders that arent suitable for storing within Postgres itself.
- A spreadsheet-like web interface for building your schemas and inspecting data.
- You can also deploy edge functions to Heroku using their [Dynos](https://www.heroku.com/dynos) runtime in conjunction with something like [Fastly](https://www.fastly.com/).
### Pricing
Simple and predictable pricing are key. The two services price quite differently, the key differences being:
- [Supabase pricing is based around usage](https://supabase.com/pricing), so you only pay for what you use.
- Heroku prices based on a tier model with [37 plans to choose from](https://elements.heroku.com/addons/heroku-postgresql#pricing).
Supabases free tier also includes a dedicated Postgres instance, and the best bit is you can upgrade to pro later without any interruptions.
### Global Deployments
You may have strict data regulations that you must comply with, so choosing your region can be very important. Heres how the deployment options stack up:
- Supabase can be deployed to any one of [12 data centers across the globe](https://github.com/supabase/supabase/discussions/4815#discussioncomment-1915129) (free tier included).
- Since Supabase is fully open source - you can also [self host wherever you like](https://supabase.com/docs/guides/hosting/overview).
- You can deploy Heroku Postgres to two data centers (US and Europe) however [6 more data centers](https://devcenter.heroku.com/articles/regions) are available on the Enterprise plan.
## How to migrate from Heroku Postgres to Supabase
Migrating is surprisingly simple. You just need to use the standard Postgres `pg_dump` and `pg_restore` tools to dump and restore from a backup. We created a handy guide for [migrating from heroku to supabase](https://github.com/supabase-community/heroku-to-supabase).
+200
View File
@@ -0,0 +1,200 @@
import { Badge, Divider, IconChevronLeft, IconFile } from '@supabase/ui'
import hydrate from 'next-mdx-remote/hydrate'
import { NextSeo } from 'next-seo'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'
import ReactMarkdown from 'react-markdown'
import CTABanner from '~/components/CTABanner'
import DefaultLayout from '~/components/Layouts/Default'
import { generateReadingTime } from '~/lib/helpers'
import authors from 'lib/authors.json'
import blogStyles from './[slug].module.css'
import { Gfm } from 'remark-gfm'
interface Props {
components: React.ReactNode
props: any
gfm: Gfm
slug: string
}
const LayoutComparison = ({ components, props, gfm, slug }: Props) => {
// @ts-ignore
const content = hydrate(props.blog.content, { components })
const authorArray = props.blog.author.split(',')
const author = []
for (let i = 0; i < authorArray.length; i++) {
author.push(
// @ts-ignore
authors.find((authors: string) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
const { basePath } = useRouter()
const NextCard = (props: any) => {
const { post, label, className } = props
return (
<Link href={`/blog/${post.url}`} as={`/blog/${post.url}`}>
<div className={className}>
<div className="border-scale-500 hover:bg-scale-100 dark:hover:bg-scale-300 cursor-pointer rounded border p-6 transition">
<div className="space-y-4">
<div>
<p className="text-scale-900 text-sm">{label}</p>
</div>
<div>
<h4 className="text-scale-1200 text-lg">{post.title}</h4>
<p className="small">{post.date}</p>
</div>
</div>
</div>
</div>
</Link>
)
}
return (
<>
<NextSeo
title={props.blog.title}
openGraph={{
title: props.blog.title,
description: props.blog.description,
url: `https://supabase.com/blog/${props.blog.slug}`,
type: 'article',
article: {
//
// to do: add expiration and modified dates
// https://github.com/garmeeh/next-seo#article
publishedTime: props.blog.date,
//
// to do: author urls should be internal in future
// currently we have external links to github profiles
authors: [props.blog.author_url],
tags: props.blog.tags.map((cat: string) => {
return cat
}),
},
images: [
{
url: `https://supabase.com${basePath}/images/blog/${
props.blog.image ? props.blog.image : props.blog.thumb
}`,
},
],
}}
/>
<DefaultLayout>
<article className="mx-auto max-w-5xl px-8 py-16 sm:px-16 xl:px-20">
{/* Title and description */}
<div className="mb-16 max-w-5xl space-y-8">
<div className="space-y-4">
<p className="text-brand-900 text-center">Alternative</p>
<h1 className="h1 text-center">{props.blog.title}</h1>
<div className="text-scale-900 flex justify-center space-x-3 text-sm">
<p>{props.blog.date}</p>
<p></p>
<p>{generateReadingTime(props.blog.content.renderedOutput)}</p>
</div>
<div className="flex justify-center gap-3">
{author.map((author: any) => {
return (
<div className="mt-6 mb-8 mr-4 w-max lg:mb-0">
<Link href={author.author_url}>
<a className="cursor-pointer">
<div className="flex items-center gap-3">
{author.author_image_url && (
<div className="w-10">
<Image
src={author.author_image_url}
className="dark:border-dark rounded-full border"
width="100%"
height="100%"
layout="responsive"
/>
</div>
)}
<div className="flex flex-col">
<span className="text-scale-1200 mb-0 text-sm">{author.author}</span>
<span className="text-scale-900 mb-0 text-xs">{author.position}</span>
</div>
</div>
</a>
</Link>
</div>
)
})}
</div>
</div>
</div>
<div className="">
{/* Content */}
<div className="prose prose-docs max-w-none">{content}</div>
<div className="py-16">
<div className="text-scale-900 dark:text-scale-1000 text-sm">Share this article</div>
<div className="mt-4 flex items-center space-x-4">
<Link
passHref
href={`https://twitter.com/share?text=${props.blog.title}&url=https://supabase.com/blog/${props.blog.slug}`}
>
<a target="_blank" className="text-scale-900 hover:text-scale-1200">
<svg
height="26"
width="26"
viewBox="-89 -46.8 644 446.8"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<path
d="m154.729 400c185.669 0 287.205-153.876 287.205-287.312 0-4.37-.089-8.72-.286-13.052a205.304 205.304 0 0 0 50.352-52.29c-18.087 8.044-37.55 13.458-57.968 15.899 20.841-12.501 36.84-32.278 44.389-55.852a202.42 202.42 0 0 1 -64.098 24.511c-18.42-19.628-44.644-31.904-73.682-31.904-55.744 0-100.948 45.222-100.948 100.965 0 7.925.887 15.631 2.619 23.025-83.895-4.223-158.287-44.405-208.074-105.504a100.739 100.739 0 0 0 -13.668 50.754c0 35.034 17.82 65.961 44.92 84.055a100.172 100.172 0 0 1 -45.716-12.63c-.015.424-.015.837-.015 1.29 0 48.903 34.794 89.734 80.982 98.986a101.036 101.036 0 0 1 -26.617 3.553c-6.493 0-12.821-.639-18.971-1.82 12.851 40.122 50.115 69.319 94.296 70.135-34.549 27.089-78.07 43.224-125.371 43.224a204.9 204.9 0 0 1 -24.078-1.399c44.674 28.645 97.72 45.359 154.734 45.359"
fillRule="nonzero"
/>
</svg>
</a>
</Link>
<Link
passHref
href={`https://www.linkedin.com/shareArticle?url=https://supabase.com/blog/${props.blog.slug}&title=${props.blog.title}`}
>
<a target="_blank" className="text-scale-900 hover:text-scale-1200">
<svg
width="20"
height="20"
viewBox="0 5 1036 990"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<path d="M0 120c0-33.334 11.667-60.834 35-82.5C58.333 15.833 88.667 5 126 5c36.667 0 66.333 10.666 89 32 23.333 22 35 50.666 35 86 0 32-11.333 58.666-34 80-23.333 22-54 33-92 33h-1c-36.667 0-66.333-11-89-33S0 153.333 0 120zm13 875V327h222v668H13zm345 0h222V622c0-23.334 2.667-41.334 8-54 9.333-22.667 23.5-41.834 42.5-57.5 19-15.667 42.833-23.5 71.5-23.5 74.667 0 112 50.333 112 151v357h222V612c0-98.667-23.333-173.5-70-224.5S857.667 311 781 311c-86 0-153 37-201 111v2h-1l1-2v-95H358c1.333 21.333 2 87.666 2 199 0 111.333-.667 267.666-2 469z" />
</svg>
</a>
</Link>
</div>
</div>
<div className="grid gap-8 py-8 lg:grid-cols-1">
<div>
{props.prevPost && <NextCard post={props.prevPost} label="Previous comparison" />}
</div>
<div>
{props.nextPost && (
<NextCard post={props.nextPost} label="Next comparison" className="text-right" />
)}
</div>
</div>
</div>
</article>
<CTABanner />
</DefaultLayout>
</>
)
}
export default LayoutComparison
+7 -1
View File
@@ -7,12 +7,14 @@ import { generateReadingTime } from './helpers'
// based on YYYY-MM-DD format
const FILENAME_SUBSTRING = 11
type Directories = '_blog' | '_case-studies'
type Directories = '_blog' | '_case-studies' | '_alternatives'
export const getSortedPosts = (directory: Directories, limit?: number, tags?: any) => {
//Finding directory named "blog" from the current working directory of Node.
const postDirectory = path.join(process.cwd(), directory)
console.log(postDirectory)
//Reads all the files in the post directory
const fileNames = fs.readdirSync(postDirectory)
@@ -109,8 +111,12 @@ export const getPostdata = async (slug: string, directory: string) => {
//Finding directory named "blog" from the current working directory of Node.
const postDirectory = path.join(process.cwd(), directory)
console.log('postDirectory', postDirectory)
const fullPath = path.join(postDirectory, `${slug}.mdx`)
console.log('fullPath', fullPath)
const postContent = fs.readFileSync(fullPath, 'utf8')
return postContent
+46
View File
@@ -0,0 +1,46 @@
/* .toc a {
text-decoration: none !important;
font-weight: 400 !important;
}
.toc > ul > li {
padding-left: 0 !important;
}
.toc li {
@apply text-sm !important;
padding-left: 1rem !important;
}
.toc ul > li:before {
content: '';
position: absolute;
background-color: none;
border-radius: 50%;
width: 0 !important;
height: 0 !important;
top: 0.6875em;
left: 0.25em;
}
.header:hover svg,
.header svg:hover {
display: inline-block;
}
.article h1,
.article h2,
.article h3,
.article h4,
.article h5 {
font-weight: 500 !important;
}
.article {
@apply mb-16;
}
.article > div > pre {
padding: 0;
background: none;
} */
+164
View File
@@ -0,0 +1,164 @@
import { Badge, Card, Divider, IconChevronLeft, IconFile, Space } from '@supabase/ui'
import matter from 'gray-matter'
import authors from 'lib/authors.json'
import hydrate from 'next-mdx-remote/hydrate'
import renderToString from 'next-mdx-remote/render-to-string'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
import Image from 'next/image'
import { useRouter } from 'next/router'
import React from 'react'
import ReactMarkdown from 'react-markdown'
import CodeBlock from '~/components/CodeBlock/CodeBlock'
import CTABanner from '~/components/CTABanner'
import DefaultLayout from '~/components/Layouts/Default'
import Quote from '~/components/Quote'
import Avatar from '~/components/Avatar'
import ImageGrid from '~/components/ImageGrid'
import { generateReadingTime } from '~/lib/helpers'
import { getAllPostSlugs, getPostdata, getSortedPosts } from '~/lib/posts'
import blogStyles from './[slug].module.css'
import LayoutComparison from '~/layouts/comparison'
// import all components used in blog articles here
// for instance, if you use a button, you must add `Button` in the components object below.
const components = {
CodeBlock,
Quote,
Avatar,
code: (props: any) => {
return <CodeBlock {...props} />
},
ImageGrid,
}
// plugins for next-mdx-remote
const gfm = require('remark-gfm')
const slug = require('rehype-slug')
// table of contents extractor
const toc = require('markdown-toc')
export async function getStaticPaths() {
console.log('slug', slug)
console.log('gfm', gfm)
const paths = getAllPostSlugs('_comparison_landing_pages')
return {
paths,
fallback: false,
}
}
export async function getStaticProps({ params }: any) {
const filePath = `${params.slug}`
const postContent = await getPostdata(filePath, '_comparison_landing_pages')
const { data, content } = matter(postContent)
const mdxSource: any = await renderToString(content, {
components,
scope: data,
mdxOptions: {
remarkPlugins: [gfm],
rehypePlugins: [slug],
},
})
const relatedPosts = getSortedPosts('_comparison_landing_pages', 5, mdxSource.scope.tags)
const allPosts = getSortedPosts('_comparison_landing_pages')
const currentIndex = allPosts
.map(function (e) {
return e.slug
})
.indexOf(filePath)
const nextPost = allPosts[currentIndex + 1]
const prevPost = allPosts[currentIndex - 1]
return {
props: {
prevPost: currentIndex === 0 ? null : prevPost ? prevPost : null,
nextPost: currentIndex === allPosts.length ? null : nextPost ? nextPost : null,
relatedPosts,
blog: {
slug: `${params.year}/${params.month}/${params.day}/${params.slug}`,
content: mdxSource,
...data,
toc: toc(content, { maxdepth: data.toc_depth ? data.toc_depth : 2 }),
},
},
}
}
function BlogPostPage(props: any) {
// @ts-ignore
const content = hydrate(props.blog.content, { components })
const authorArray = props.blog.author.split(',')
const author = []
for (let i = 0; i < authorArray.length; i++) {
author.push(
// @ts-ignore
authors.find((authors: string) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
const { basePath } = useRouter()
const NextCard = (props: any) => {
const { post, label, className } = props
return (
<Link href={`/blog/${post.url}`} as={`/blog/${post.url}`}>
<div className={className}>
<div className="border-scale-500 hover:bg-scale-100 dark:hover:bg-scale-300 cursor-pointer rounded border p-6 transition">
<div className="space-y-4">
<div>
<p className="text-scale-900 text-sm">{label}</p>
</div>
<div>
<h4 className="text-scale-1200 text-lg">{post.title}</h4>
<p className="small">{post.date}</p>
</div>
</div>
</div>
</div>
</Link>
)
}
const toc = props.blog.toc && (
<div className="space-y-8 py-8 lg:py-0">
<div>
<div className="space-x-2">
{props.blog.tags.map((tag: string) => {
return (
<a href={`/blog/tags/${tag}`} key={`category-badge-${tag}`}>
<Badge>{tag}</Badge>
</a>
)
})}
</div>
</div>
<div>
<p className="text-scale-1200">On this page</p>
<div>
<div className={[blogStyles['toc'], 'prose prose-toc'].join(' ')}>
<ReactMarkdown plugins={[gfm]}>{props.blog.toc.content}</ReactMarkdown>
</div>
</div>
</div>
</div>
)
return <LayoutComparison components={components} props={props} gfm={gfm} slug={slug} />
}
// function BlogPostPage() {
// return <h1>blog post</h1>
// }
export default BlogPostPage
@@ -0,0 +1,46 @@
/* .toc a {
text-decoration: none !important;
font-weight: 400 !important;
}
.toc > ul > li {
padding-left: 0 !important;
}
.toc li {
@apply text-sm !important;
padding-left: 1rem !important;
}
.toc ul > li:before {
content: '';
position: absolute;
background-color: none;
border-radius: 50%;
width: 0 !important;
height: 0 !important;
top: 0.6875em;
left: 0.25em;
}
.header:hover svg,
.header svg:hover {
display: inline-block;
}
.article h1,
.article h2,
.article h3,
.article h4,
.article h5 {
font-weight: 500 !important;
}
.article {
@apply mb-16;
}
.article > div > pre {
padding: 0;
background: none;
} */
+107
View File
@@ -0,0 +1,107 @@
import matter from 'gray-matter'
import authors from 'lib/authors.json'
import hydrate from 'next-mdx-remote/hydrate'
import renderToString from 'next-mdx-remote/render-to-string'
import React from 'react'
import Avatar from '~/components/Avatar'
import CodeBlock from '~/components/CodeBlock/CodeBlock'
import ImageGrid from '~/components/ImageGrid'
import Quote from '~/components/Quote'
import LayoutComparison from '~/layouts/comparison'
import { getAllPostSlugs, getPostdata, getSortedPosts } from '~/lib/posts'
// import all components used in blog articles here
// for instance, if you use a button, you must add `Button` in the components object below.
const components = {
CodeBlock,
Quote,
Avatar,
code: (props: any) => {
return <CodeBlock {...props} />
},
ImageGrid,
}
// plugins for next-mdx-remote
const gfm = require('remark-gfm')
const slug = require('rehype-slug')
// table of contents extractor
const toc = require('markdown-toc')
export async function getStaticPaths() {
console.log('slug', slug)
console.log('gfm', gfm)
const paths = getAllPostSlugs('_alternatives')
return {
paths,
fallback: false,
}
}
export async function getStaticProps({ params }: any) {
const filePath = `${params.slug}`
const postContent = await getPostdata(filePath, '_alternatives')
const { data, content } = matter(postContent)
const mdxSource: any = await renderToString(content, {
components,
scope: data,
mdxOptions: {
remarkPlugins: [gfm],
rehypePlugins: [slug],
},
})
const relatedPosts = getSortedPosts('_alternatives', 5, mdxSource.scope.tags)
const allPosts = getSortedPosts('_alternatives')
const currentIndex = allPosts
.map(function (e) {
return e.slug
})
.indexOf(filePath)
const nextPost = allPosts[currentIndex + 1]
const prevPost = allPosts[currentIndex - 1]
return {
props: {
prevPost: currentIndex === 0 ? null : prevPost ? prevPost : null,
nextPost: currentIndex === allPosts.length ? null : nextPost ? nextPost : null,
relatedPosts,
blog: {
slug: `${params.year}/${params.month}/${params.day}/${params.slug}`,
content: mdxSource,
...data,
toc: toc(content, { maxdepth: data.toc_depth ? data.toc_depth : 2 }),
},
},
}
}
function BlogPostPage(props: any) {
// @ts-ignore
const content = hydrate(props.blog.content, { components })
const authorArray = props.blog.author.split(',')
const author = []
for (let i = 0; i < authorArray.length; i++) {
author.push(
// @ts-ignore
authors.find((authors: string) => {
// @ts-ignore
return authors.author_id === authorArray[i]
})
)
}
return <LayoutComparison components={components} props={props} gfm={gfm} slug={slug} />
}
// function BlogPostPage() {
// return <h1>blog post</h1>
// }
export default BlogPostPage
+1
View File
@@ -18,6 +18,7 @@ module.exports = ui({
'../../packages/common/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
'./components/**/*.tsx',
'./layouts/**/*.tsx',
'./pages/**/*.tsx',
'./_blog/*.mdx',
// purge styles from supabase ui theme