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

feat: Migrate into Next.js App Router #353

Merged
merged 15 commits into from
Oct 25, 2023
Merged
14 changes: 0 additions & 14 deletions .eslintrc

This file was deleted.

17 changes: 17 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
extends: ["next/core-web-vitals", "prettier"],
plugins: ["chakra-ui"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
},
rules: {
"sort-imports": "error",
"@next/next/no-img-element": "off",
"chakra-ui/props-order": "error",
"chakra-ui/props-shorthand": "error",
"chakra-ui/require-specific-component": "error",
},
ignorePatterns: [".eslintrc.js"],
};
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ jobs:
- name: Run build
run: |
yarn build
yarn export
env:
NEXT_PUBLIC_BASE_URL: ${{ secrets.BASE_URL }}
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: ${{ secrets.GOOGLE_ANALYTICS_ID }}
Expand Down
6 changes: 1 addition & 5 deletions .stylelintrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
{
"extends": [
"stylelint-config-sass-guidelines",
"stylelint-config-recess-order",
"stylelint-config-prettier"
],
"extends": ["stylelint-config-sass-guidelines", "stylelint-config-recess-order"],
"rules": {
"order/properties-alphabetical-order": null
}
Expand Down
10 changes: 10 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @ts-check

/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
output: "export",
};

module.exports = nextConfig;
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next export",
"lint:code": "next lint",
"lint:code:fix": "next lint --fix",
"lint:style": "stylelint 'src/**/*.{css,scss}'",
Expand Down Expand Up @@ -44,6 +43,7 @@
"@types/node": "^18.11.10",
"@types/react": "^18.2.21",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/parser": "^6.7.5",
"@vitejs/plugin-react": "^3.1.0",
"eslint": "8.37.0",
"eslint-config-next": "13.5.3",
Expand All @@ -60,7 +60,6 @@
"react-test-renderer": "^18.2.0",
"sass": "^1.62.1",
"stylelint": "^15.10.3",
"stylelint-config-prettier": "^9.0.4",
"stylelint-config-recess-order": "^4.3.0",
"stylelint-config-sass-guidelines": "^10.0.0",
"ts-node": "^10.9.1",
Expand Down
20 changes: 20 additions & 0 deletions src/app/blog/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BlogInfo, BlogInfos, getAllBlogInfos, getBlogFromId } from "../../lib/blog-fetch";
Fixed Show fixed Hide fixed
import { Metadata } from "next";
import { Post } from "./post";

export async function generateMetadata({ params }: { params: BlogInfo }): Promise<Metadata> {
const { title } = await getBlogFromId(params.id);
return { title: `ブログ - ${title}` };
}

export async function generateStaticParams(): Promise<BlogInfos> {
return getAllBlogInfos();
}

export default async function Page({ params }: { params?: BlogInfo }) {
if (params == null) {
throw new Error("invalid params");
}
const post = await getBlogFromId(params.id);
return <Post post={post} />;
}
34 changes: 7 additions & 27 deletions src/pages/blog/[id].tsx → src/app/blog/[id]/post.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Blog, BlogInfo, getAllBlogInfos, getBlogFromId } from "../../lib/blog-fetch";
"use client";

Dismissed Show dismissed Hide dismissed
import { Container, Heading, Link, Text, VStack } from "@chakra-ui/react";
import type { GetStaticPaths, GetStaticProps, NextPage } from "next";
import { Blog } from "../../lib/blog-fetch";
import { DateString } from "../../components/date";
import { Layout } from "../../components/layout";
import MarkdownIt from "markdown-it";
import MarkdownItFootnote from "markdown-it-footnote";
import MarkdownItFrontMatter from "markdown-it-front-matter";
Expand All @@ -17,11 +17,11 @@ const md = MarkdownIt({
.use(MarkdownItFrontMatter)
.use(MarkdownItFootnote);

type BlogPostPageProps = {
export type BlogPostPageProps = {
post: Blog;
};

const BlogPostPage: NextPage<BlogPostPageProps> = ({ post }) => {
export const Post = ({ post }: BlogPostPageProps) => {
const prevNext = (
<PrevNextLink
prevLinkHref={post.prevId !== "" ? post.prevId : null}
Expand All @@ -30,7 +30,7 @@ const BlogPostPage: NextPage<BlogPostPageProps> = ({ post }) => {
);
const bodyHtml = md.render(emojify`${post.content}`);
return (
<Layout pageName={`限界開発鯖 - ブログ - ${post.title}`}>
<>
<VStack>
<Heading m={8} textAlign="center">
{post.title}
Expand All @@ -48,26 +48,6 @@ const BlogPostPage: NextPage<BlogPostPageProps> = ({ post }) => {
<div dangerouslySetInnerHTML={{ __html: bodyHtml }} />
</Container>
{prevNext}
</Layout>
</>
);
};

export const getStaticProps: GetStaticProps<BlogPostPageProps, BlogInfo> = async ({ params }) => {
if (params == null) {
throw new Error("invalid params");
}
console.dir(params);
const post = await getBlogFromId(params.id);
return {
props: {
post,
},
};
};

export const getStaticPaths: GetStaticPaths<BlogInfo> = async () => {
const paths = await getAllBlogInfos();
return { paths, fallback: false };
};

export default BlogPostPage;
50 changes: 50 additions & 0 deletions src/app/blog/blog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";
Dismissed Show dismissed Hide dismissed
import {
Avatar,
Button,
Flex,
HStack,
Heading,
Link,
SimpleGrid,
Spacer,
VStack,
} from "@chakra-ui/react";
import { DateString } from "../components/date";
import { Metadata } from "../lib/blog-fetch";
import NextLink from "next/link";
import { Metadata as NextMetadata } from "next";
import type { NextPage } from "next";

export const metadata: NextMetadata = {
title: "限界開発鯖 - ブログ",
};

const BlogCard = ({ id, title, date, author, authorId }: Metadata): JSX.Element => (
<HStack borderColor="shadowed" borderRightWidth="1px" borderBottomWidth="2px">
<Avatar as={NextLink} flex="0 0 sm" href={`/blog/${id}`} name={title} />
<VStack alignItems="self-start" flex="1 1" p={2} spacing="0.5">
<NextLink href={`/blog/${id}`}>
<Heading as="h3" fontSize="lg">
{title}
</Heading>
</NextLink>
<Flex align="self-end" wrap="wrap" gap={2} w="100%">
<Link href={`https://github.com/${authorId}`}>{author}</Link>
<DateString dateString={date} />
<Spacer />
<Button as={NextLink} href={`/blog/${id}`} size="sm">
記事を読む &rarr;
</Button>
</Flex>
</VStack>
</HStack>
);

export const Blog: NextPage<{ blogs: Metadata[] }> = ({ blogs }) => (
<SimpleGrid gap={4} columns={1}>
{blogs.map((blog) => (
<BlogCard key={blog.id} {...blog} />
))}
</SimpleGrid>
);
7 changes: 7 additions & 0 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Blog } from "./blog";
import { getSortedBlogMetadata } from "../lib/blog-fetch";

export default async function Page() {
const blogs = await getSortedBlogMetadata();
return <Blog blogs={blogs} />;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from "vitest";
import { DateString } from "./date";
import { render } from "../utils/react-test";
import { render } from "../../utils/react-test";

it("renders correctly", () => {
const tree = render(<DateString dateString="2021-01-01" />);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from "vitest";
import { Footer } from "./footer";
import { render } from "../utils/react-test";
import { render } from "../../utils/react-test";

it("renders correctly", () => {
const tree = render(<Footer />);
Expand Down
10 changes: 5 additions & 5 deletions src/components/footer.tsx → src/app/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";
Dismissed Show dismissed Hide dismissed

import { Box, Link } from "@chakra-ui/react";

import NextLink from "next/link";
Expand All @@ -8,11 +10,9 @@ export const Footer = (): JSX.Element => {
const year = useContext(yearContext);
return (
<Box as="footer" p={4} textAlign="center" borderColor="shadowed" borderTopWidth="2px">
<NextLink href="/policy" passHref legacyBehavior>
<Link mr={2} color="highlighted" fontStyle="normal">
プライバシーポリシー
</Link>
</NextLink>
<Link as={NextLink} mr={2} color="highlighted" fontStyle="normal" href="/policy">
プライバシーポリシー
</Link>
- (c) {year} Approvers
</Box>
);
Expand Down
43 changes: 43 additions & 0 deletions src/app/components/google-analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use client";
Dismissed Show dismissed Hide dismissed

import { usePathname, useSearchParams } from "next/navigation";
import Script from "next/script";
import { useEffect } from "react";

export const GOOGLE_ANALYTICS_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID ?? "";

const notifyShowingPage = (url: string): void => {
(
window as unknown as { gtag(event: "config", id: string, options: { page_path: string }): void }
).gtag("config", GOOGLE_ANALYTICS_ID, {
page_path: url,
});
};

export const GoogleAnalytics = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
notifyShowingPage(`${pathname}?${searchParams}`);
}, [pathname, searchParams]);
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_ID}`}
/>
<Script strategy="afterInteractive" id="send-ga">
{`
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "${GOOGLE_ANALYTICS_ID}", {
page_path: window.location.pathname,
});
`}
</Script>
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from "vitest";
import { Header } from "./header";
import { render } from "../utils/react-test";
import { render } from "../../utils/react-test";

it("renders correctly", () => {
const tree = render(<Header />);
Expand Down
18 changes: 18 additions & 0 deletions src/app/components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Avatar, Center, Flex, Heading } from "@chakra-ui/react";
import NextLink from "next/link";

export const Header = (): JSX.Element => (
<Center as="header" w="100%" p={4} borderColor="shadowed" borderBottomWidth="2px">
<Flex as={NextLink} align="center" href="/">
<Avatar
w={8}
h={8}
mr={2}
borderRadius="37%"
name="Approversロゴ"
src="/android-chrome-192x192.png"
/>
<Heading as="span">Approvers</Heading>
</Flex>
</Center>
);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from "vitest";
import { Navigation } from "./navigation";
import { render } from "../utils/react-test";
import { render } from "../../utils/react-test";

it("renders correctly", () => {
const tree = render(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";
Dismissed Show dismissed Hide dismissed

import {
Button,
Container,
Expand Down Expand Up @@ -29,11 +31,9 @@ const Links = ({ links }: LinksProps): JSX.Element => (
<VStack>
{links.map(({ name, url }) => (
<Container key={name}>
<NextLink href={url} passHref>
<Button as="a" minW="100%">
{name}
</Button>
</NextLink>
<Button as={NextLink} minW="100%" href={url}>
{name}
</Button>
</Container>
))}
</VStack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from "vitest";
import { PrevNextLink } from "./prev-next-link";
import { render } from "../utils/react-test";
import { render } from "../../utils/react-test";

it("renders both prev and next correctly", () => {
const tree = render(
Expand Down
Loading
Loading