Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Blog using Contentlayer #72

Merged
merged 1 commit into from
Aug 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ yarn-error.log*
**/public/workbox-*.js.map
**/public/worker-*.js.map
**/public/fallback-*.js
**/public/*.gz
**/public/*.gz

# Contentlayer
.contentlayer
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ out
**/public/workbox-*.js.map
**/public/worker-*.js.map
**/public/fallback-*.js
**/public/*.gz
**/public/*.gz

# Contentlayer
.contentlayer
28 changes: 28 additions & 0 deletions components/MDX/Components.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as FutureImage from "next/future/image";
import { CustomLink } from "@components/elements/CustomLink";

function Image(props) {
return <FutureImage alt={props.alt} {...props} />;
}

function RoundedImage(props) {
return <FutureImage alt={props.alt} className="rounded-lg" {...props} />;
}

function Callout(props) {
return (
<div className="my-8 flex rounded-lg bg-gray-200 p-4 dark:bg-gray-800">
<div className="mr-4 flex w-4 items-center">{props.emoji}</div>
<div className="callout w-full">{props.children}</div>
</div>
);
}

const MDXComponents = {
Image,
RoundedImage,
Link: CustomLink,
Callout,
};

export default MDXComponents;
22 changes: 22 additions & 0 deletions components/blog/BlogPost.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Link from "next/link";
import { ChevronRightIcon } from "@heroicons/react/outline";
import { parseISO, format } from "date-fns";

export function BlogPost({ title, summary, slug, publishedAt }) {
return (
<Link href={`/blog/${slug}`}>
<a className="w-full">
<div className="group flex w-full flex-col justify-between rounded-md bg-zinc-200/[25%] px-6 py-4 duration-200 hover:bg-zinc-200/60 motion-reduce:transition-none dark:bg-white/[10%] dark:text-white dark:hover:bg-white/[15%]">
<div className="flex flex-row justify-between">
<h4 className="relative flex w-full items-center text-lg font-medium text-gray-900 dark:text-gray-100 md:text-xl">
{title}
<ChevronRightIcon className="inline-block h-4 w-4 translate-x-[1px] opacity-0 duration-200 group-hover:translate-x-[5px] group-hover:opacity-100 motion-reduce:transition-none" />
</h4>
<p className="w-2/4 text-left text-gray-500 duration-200 motion-reduce:transition-none md:mb-0 md:text-right">{format(parseISO(publishedAt), "MMMM dd, yyyy")}</p>
</div>
<p className="mt-2 text-slate-600 duration-200 motion-reduce:transition-none dark:text-slate-400">{summary || "Nothing to say..."}</p>
</div>
</a>
</Link>
);
}
20 changes: 20 additions & 0 deletions components/elements/CustomLink.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Link from "next/link";

export function CustomLink({ href, ...rest }) {
const isInternalLink = href && href.startsWith("/");
const isAnchorLink = href && href.startsWith("#");

if (isInternalLink) {
return (
<Link href={href}>
<a {...rest} />
</Link>
);
}

if (isAnchorLink) {
return <a href={href} {...rest} />;
}

return <a target="_blank" rel="noopener noreferrer" href={href} {...rest} />;
}
1 change: 0 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export const nav = {
{
href: "/blog/",
title: "Blog",
target: "_blank",
},
{
href: "/discord/",
Expand Down
67 changes: 67 additions & 0 deletions contentlayer.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { defineDocumentType, makeSource } from "contentlayer/source-files";
import readingTime from "reading-time";
import remarkGfm from "remark-gfm";
import rehypeSlug from "rehype-slug";
import rehypeCodeTitles from "rehype-code-titles";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypePrism from "rehype-prism-plus";
const headers_regex = /(#{1,6})\s+(.+)/g;

const computedFields = {
readingTime: { type: "json", resolve: (doc) => readingTime(doc.body.raw) },
wordCount: {
type: "number",
resolve: (doc) => doc.body.raw.split(/\s+/gu).length,
},
slug: {
type: "string",
resolve: (doc) => doc._raw.sourceFileName.replace(/\.mdx$/, ""),
},
};

const Blog = defineDocumentType(() => ({
name: "Blog",
filePathPattern: "blog/*.mdx",
contentType: "mdx",
fields: {
title: { type: "string", required: true },
publishedAt: { type: "string", required: true },
author: { type: "string", required: true },
summary: { type: "string", required: true },
image: { type: "string", required: false },
},
computedFields,
}));

const OtherPage = defineDocumentType(() => ({
name: "OtherPage",
filePathPattern: "*.mdx",
contentType: "mdx",
fields: {
title: { type: "string", required: true },
},
computedFields,
}));

const contentLayerConfig = makeSource({
contentDirPath: "data",
documentTypes: [Blog, OtherPage],
mdx: {
remarkPlugins: [remarkGfm],
rehypePlugins: [
rehypeSlug,
rehypeCodeTitles,
rehypePrism,
[
rehypeAutolinkHeadings,
{
properties: {
className: ["anchor"],
},
},
],
],
},
});

export default contentLayerConfig;
4 changes: 3 additions & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"@components/*": ["components/*"],
"@lib/*": ["lib/*"],
"@styles/*": ["styles/*"],
"@/*": ["*"]
"@/*": ["*"],
"contentlayer/generated": ["./.contentlayer/generated"]
}
},
"include": ["**/*.js", "**/*.jsx", ".contentlayer/generated"],
"exclude": ["node_modules"]
}
168 changes: 83 additions & 85 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,95 @@
/** @type {import('next').NextConfig} */
const { withPlugins } = require("next-composed-plugins");
const { withContentlayer } = require("next-contentlayer");
const withPWA = require("next-pwa");
const CompressionPlugin = require("compression-webpack-plugin");
const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");

//const withBundleAnalyzer = require("@next/bundle-analyzer")({
// enabled: process.env.ANALYZE === "true",
//});

//module.exports = withBundleAnalyzer({});

module.exports = withPWA({
pwa: {
dest: "public",
disable: process.env.NODE_ENV === "development",
register: true,
},
reactStrictMode: true,
pageExtensions: ["mdx", "md", "jsx", "js"],
poweredByHeader: false,
trailingSlash: true,
compress: true,
swcMinify: false,
images: {
domains: [
"github.githubassets.com", // GitHub assets
],
},
experimental: { images: { allowFutureImage: true } },
async redirects() {
return [
{
source: "/discord",
destination: "https://discord.gg/uxtSMtd2xZ",
permanent: true,
},
{
source: "/twitter",
destination: "https://twitter.com/majonezexe",
permanent: true,
},
{
source: "/instagram",
destination: "https://www.instagram.com/majonezexe/",
permanent: true,
},
{
source: "/blog",
destination: "https://igorkowalczyk.github.io/blog/",
permanent: true,
},
{
source: "/arc-sw.js",
destination: "https://arc.io/arc-sw.js",
permanent: true,
},
{
source: "/r/:path*",
destination: "/:path*",
permanent: true,
},
{
source: "/discord-server",
destination: "/discord",
permanent: true,
},
];
},
async headers() {
return [
{
source: "/(.*)",
headers: securityHeaders,
},
];
},
webpack: (config, { isServer, dev, config: { distDir } }) => {
if (!isServer && !dev) {
config.plugins.push(
new CompressionPlugin(),
new LodashModuleReplacementPlugin(),
new webpack.DefinePlugin({
"process.env.ASSET_PATH": JSON.stringify("./public/"),
"process.env.VERSION": JSON.stringify(process.env.npm_package_version),
})
),
(config.optimization.minimizer = [new TerserPlugin()]);
}
return config;
},
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});

module.exports = withPlugins(
withPWA({
pwa: {
dest: "public",
disable: process.env.NODE_ENV === "development",
register: true,
},
reactStrictMode: true,
pageExtensions: ["mdx", "md", "jsx", "js"],
poweredByHeader: false,
trailingSlash: true,
compress: true,
swcMinify: false,
images: {
domains: [
"github.githubassets.com", // GitHub assets
],
},
experimental: { images: { allowFutureImage: true } },
async redirects() {
return [
{
source: "/discord",
destination: "https://discord.gg/uxtSMtd2xZ",
permanent: true,
},
{
source: "/twitter",
destination: "https://twitter.com/majonezexe",
permanent: true,
},
{
source: "/instagram",
destination: "https://www.instagram.com/majonezexe/",
permanent: true,
},
{
source: "/arc-sw.js",
destination: "https://arc.io/arc-sw.js",
permanent: true,
},
{
source: "/r/:path*",
destination: "/:path*",
permanent: true,
},
{
source: "/discord-server",
destination: "/discord",
permanent: true,
},
];
},
async headers() {
return [
{
source: "/(.*)",
headers: securityHeaders,
},
];
},
webpack: (config, { isServer, dev, config: { distDir } }) => {
if (!isServer && !dev) {
config.plugins.push(
new CompressionPlugin(),
new LodashModuleReplacementPlugin(),
new webpack.DefinePlugin({
"process.env.ASSET_PATH": JSON.stringify("./public/"),
"process.env.VERSION": JSON.stringify(process.env.npm_package_version),
})
),
(config.optimization.minimizer = [new TerserPlugin()]);
}
return config;
},
}),
[withBundleAnalyzer, withContentlayer]
);

const ContentSecurityPolicy = `
default-src 'self' *.googletagmanager.com *.arc.io;
script-src 'self' 'unsafe-eval' 'unsafe-inline' *.googletagmanager.com arc.io *.arc.io *.sentry-cdn.com;
Expand Down
Loading