Skip to content

Commit 57a5e17

Browse files
authored
Update to Next.js 13, React 18 (tokio-rs#688)
1 parent 1a27bef commit 57a5e17

29 files changed

+3434
-7053
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tutorial-code/Cargo.lock
1616
# next.js
1717
/.next/
1818
/out/
19+
/dist/
1920

2021
# production
2122
/build

components/content.tsx

Lines changed: 52 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,8 @@
11
import Menu from "../components/menu";
2-
import classnames from "classnames";
32
import { DiscordIcon, GitHubIcon } from "./icons";
4-
import React from "react";
5-
import ReactMarkdown from "react-markdown/with-html";
6-
import SyntaxHighlighter from "react-syntax-highlighter";
7-
import GithubSlugger from "github-slugger";
8-
import CustomBlocks from "remark-custom-blocks";
3+
import React, { useEffect, useMemo, useRef, useState } from "react";
94
import Link from "next/link";
105

11-
const CodeBlock = ({ language, value }) => {
12-
// Remove lines starting with `# `. This is code to make the doc tests pass
13-
// but should not be displayed.
14-
value = value
15-
.split("\n")
16-
.filter((line) => !line.startsWith("# "))
17-
.join("\n");
18-
19-
return (
20-
<SyntaxHighlighter useInlineStyles={false} language={language}>
21-
{value}
22-
</SyntaxHighlighter>
23-
);
24-
};
25-
26-
const Blocks = {
27-
warning: {
28-
classes: "is-warning",
29-
},
30-
info: {
31-
classes: "is-info",
32-
},
33-
};
34-
35-
function Heading(slugger, headings, props) {
36-
let children = React.Children.toArray(props.children);
37-
let text = children.reduce(flatten, "");
38-
let slug = slugger.slug(text);
39-
headings.push({ level: props.level, title: text, slug });
40-
return React.createElement("h" + props.level, { id: slug }, props.children);
41-
}
42-
43-
function Block({
44-
children,
45-
data: {
46-
hProperties: { className },
47-
},
48-
}) {
49-
return (
50-
<blockquote className={classnames(...className)}>{children}</blockquote>
51-
);
52-
}
53-
54-
function BlockBody({ children }) {
55-
return children;
56-
}
57-
58-
function flatten(text, child) {
59-
return typeof child === "string"
60-
? text + child
61-
: React.Children.toArray(child.props.children).reduce(flatten, text);
62-
}
63-
646
function Footer({ next, prev, mdPath }) {
657
let edit = `https://github.com/tokio-rs/website/edit/master/content/${mdPath}`;
668
return (
@@ -127,32 +69,33 @@ function insertHeading(heading, menu, level = 1) {
12769
}
12870

12971
function TableOfContents({ headings }) {
130-
let menu = [];
72+
const list = useMemo(() => {
73+
let menu = [];
74+
for (const heading of headings) {
75+
insertHeading(heading, menu);
76+
}
13177

132-
for (const heading of headings) {
133-
insertHeading(heading, menu);
134-
}
78+
return menu.map((entry) => {
79+
const heading = entry.heading;
13580

136-
const list = menu.map((entry) => {
137-
const heading = entry.heading;
81+
let nested = entry.nested.map((entry) => {
82+
const heading = entry.heading;
13883

139-
let nested = entry.nested.map((entry) => {
140-
const heading = entry.heading;
84+
return (
85+
<li key={heading.slug}>
86+
<a href={`#${heading.slug}`}>{heading.title}</a>
87+
</li>
88+
);
89+
});
14190

14291
return (
14392
<li key={heading.slug}>
14493
<a href={`#${heading.slug}`}>{heading.title}</a>
94+
{entry.nested.length > 0 && <ul>{nested}</ul>}
14595
</li>
14696
);
14797
});
148-
149-
return (
150-
<li key={heading.slug}>
151-
<a href={`#${heading.slug}`}>{heading.title}</a>
152-
{entry.nested.length > 0 && <ul>{nested}</ul>}
153-
</li>
154-
);
155-
});
98+
}, [headings]);
15699

157100
return (
158101
<aside className="column is-one-third tk-content-summary">
@@ -161,46 +104,52 @@ function TableOfContents({ headings }) {
161104
);
162105
}
163106

164-
export default function Content({ menu, href, title, next, prev, body, mdPath }) {
165-
const slugger = new GithubSlugger();
166-
let headings = [{ level: 1, title, slug: "" }];
167-
const HeadingRenderer = (props) => {
168-
return Heading(slugger, headings, props);
169-
};
107+
export default function Content({
108+
menu,
109+
href,
110+
title,
111+
next,
112+
prev,
113+
body,
114+
mdPath,
115+
}) {
116+
const isBlogRoute = href.startsWith("/blog");
117+
118+
const [headings, setHeadings] = useState([]);
119+
const mdRef = useRef<HTMLDivElement>(null);
170120

171-
let isBlogRoute = href.startsWith("/blog");
121+
useEffect(() => {
122+
mdRef.current?.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((el) => {
123+
const level = Number(el.tagName.slice(-1));
124+
const title = el.textContent;
125+
const slug = el.id;
126+
setHeadings((headings) => [...headings, { level, title, slug }]);
127+
});
128+
return () => {
129+
setHeadings([]);
130+
};
131+
}, [mdRef]);
172132

173133
return (
174134
<>
175135
<div className="columns is-marginless tk-docs">
176136
<div className="column is-one-quarter tk-docs-nav">
177137
<Menu href={href} menu={menu}>
178-
{ isBlogRoute && (
179-
<div className="all-posts-link">
180-
<Link href="/blog"><a>More Blog Posts</a></Link>
181-
</div>
182-
)
183-
}
138+
{isBlogRoute && (
139+
<div className="all-posts-link">
140+
<Link href="/blog">More Blog Posts</Link>
141+
</div>
142+
)}
184143
</Menu>
185144
</div>
186145
<div className="column is-three-quarters tk-content">
187146
<section className="section content">
188147
<div className="columns">
189-
<div className="column is-two-thirds tk-markdown">
190-
<h1 className="title">{title}</h1>
191-
<ReactMarkdown
192-
escapeHtml={false}
193-
source={body}
194-
renderers={{
195-
code: CodeBlock,
196-
heading: HeadingRenderer,
197-
warningCustomBlock: Block,
198-
warningCustomBlockBody: BlockBody,
199-
infoCustomBlock: Block,
200-
infoCustomBlockBody: BlockBody,
201-
}}
202-
plugins={[[CustomBlocks, Blocks]]}
203-
/>
148+
<div className="column is-two-thirds tk-markdown" ref={mdRef}>
149+
<h1 className="title" id="">
150+
{title}
151+
</h1>
152+
<div dangerouslySetInnerHTML={{ __html: body }}></div>
204153
<Footer next={next} prev={prev} mdPath={mdPath} />
205154
</div>
206155
<TableOfContents headings={headings} />

components/footer.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,31 @@ export default function Footer() {
6161
</div>
6262
<div className="container has-text-centered">
6363
<p>
64-
Built with all the love in the world by <a href="https://twitter.com/carllerche" target="_blank"
65-
rel="noopener">@carllerche</a>
64+
Built with all the love in the world by{" "}
65+
<a
66+
href="https://twitter.com/carllerche"
67+
target="_blank"
68+
rel="noopener"
69+
>
70+
@carllerche
71+
</a>
6672
</p>
6773
</div>
6874
<div className="container has-text-centered">
6975
<p>
70-
with the help of <a
71-
href="https://github.com/tokio-rs/tokio/graphs/contributors">our contributors</a>.
76+
with the help of{" "}
77+
<a href="https://github.com/tokio-rs/tokio/graphs/contributors">
78+
our contributors
79+
</a>
80+
.
7281
</p>
7382
</div>
7483
<div className="container has-text-centered">
7584
<p>
76-
Hosted by <a href="https://netlify.com" rel="sponsored nofollow">Netlify</a>
85+
Hosted by{" "}
86+
<a href="https://netlify.com" rel="sponsored nofollow">
87+
Netlify
88+
</a>
7789
</p>
7890
</div>
7991
</div>

components/layout.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import React, { FC, ReactNode } from "react";
22

33
import Head from "next/head";
44
import Navigation from "./nav";
5+
import { Roboto } from "@next/font/google";
6+
7+
const roboto = Roboto({
8+
weight: ["300", "400", "500", "700"],
9+
display: "swap",
10+
subsets: ["latin"],
11+
});
512

613
type Props = {
714
title?: string;
@@ -19,19 +26,22 @@ const Layout: FC<Props> = ({ title, blog, children }) => (
1926
<Head>
2027
<title>{pageTitle(title)}</title>
2128
<meta name="viewport" content="width=device-width, initial-scale=1" />
22-
<meta name="description" content="Tokio is a runtime for writing reliable asynchronous applications with Rust. It provides async I/O, networking, scheduling, timers, and more."></meta>
29+
<meta
30+
name="description"
31+
content="Tokio is a runtime for writing reliable asynchronous applications with Rust. It provides async I/O, networking, scheduling, timers, and more."
32+
></meta>
2333
<link rel="alternate icon" type="image/png" href="/favicon-32x32.png" />
2434
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
25-
<link rel="alternate" type="application/rss+xml" href="/_next/static/feed.xml" />
26-
2735
<link
28-
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap"
29-
rel="stylesheet"
36+
rel="alternate"
37+
type="application/rss+xml"
38+
href="/_next/static/feed.xml"
3039
/>
31-
<script defer src="/tk-stack.js"></script>
3240
</Head>
33-
<Navigation blog={blog} />
34-
{children}
41+
<main className={roboto.className}>
42+
<Navigation blog={blog} />
43+
{children}
44+
</main>
3545
</>
3646
);
3747

components/libs.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,12 @@ const LIBS: Library[] = [
1111
{
1212
id: "runtime",
1313
name: "Reliable",
14-
desc:
15-
"Tokio's APIs are memory-safe, thread-safe, and misuse-resistant. This helps prevent common bugs, such as unbounded queues, buffer overflows, and task starvation.",
14+
desc: "Tokio's APIs are memory-safe, thread-safe, and misuse-resistant. This helps prevent common bugs, such as unbounded queues, buffer overflows, and task starvation.",
1615
},
1716
{
1817
id: "hyper",
1918
name: "Fast",
20-
desc:
21-
"Building on top of Rust, Tokio provides a multi-threaded, work-stealing scheduler. Applications can process hundreds of thousands of requests per second with minimal overhead.",
19+
desc: "Building on top of Rust, Tokio provides a multi-threaded, work-stealing scheduler. Applications can process hundreds of thousands of requests per second with minimal overhead.",
2220
},
2321
{
2422
id: "tonic",
@@ -34,8 +32,7 @@ const LIBS: Library[] = [
3432
{
3533
id: "tower",
3634
name: "Flexible",
37-
desc:
38-
"The needs of a server application differ from that of an embedded device. Although Tokio comes with defaults that work well out of the box, it also provides the knobs needed to fine tune to different cases.",
35+
desc: "The needs of a server application differ from that of an embedded device. Although Tokio comes with defaults that work well out of the box, it also provides the knobs needed to fine tune to different cases.",
3936
},
4037
];
4138

components/menu.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useState } from "react";
1+
import { Fragment, useCallback, useState } from "react";
22
import classnames from "classnames";
33
import Link from "next/link";
44

@@ -20,10 +20,10 @@ const monthNames = [
2020
export default function Menu({ href, menu, children }) {
2121
const groups = menu.map(({ key, title, nested }) => {
2222
return (
23-
<React.Fragment key={key}>
23+
<Fragment key={key}>
2424
<p className="menu-label">{title}</p>
2525
<Level1 href={href} menu={nested} />
26-
</React.Fragment>
26+
</Fragment>
2727
);
2828
});
2929

@@ -120,11 +120,11 @@ function Level2({ href, menu }) {
120120

121121
if (menu.data.subtitle) {
122122
const className = href == menu.href ? "is-active" : "";
123-
items.unshift((
123+
items.unshift(
124124
<li key={menu.key} className={className}>
125125
<a href={menu.href}>{menu.data.subtitle}</a>
126126
</li>
127-
));
127+
);
128128
}
129129

130130
return <ul>{items}</ul>;

0 commit comments

Comments
 (0)