mirror of
https://github.com/hsjobeki/noogle.git
synced 2024-09-11 21:07:53 +03:00
improve SEO
This commit is contained in:
parent
53912303f3
commit
f938e1f95a
@ -3,7 +3,7 @@ import { ShareButton } from "@/components/ShareButton";
|
|||||||
import { BackButton } from "@/components/BackButton";
|
import { BackButton } from "@/components/BackButton";
|
||||||
import { Doc, data, manualLinks } from "@/models/data";
|
import { Doc, data, manualLinks } from "@/models/data";
|
||||||
import { getPrimopDescription } from "@/models/primop";
|
import { getPrimopDescription } from "@/models/primop";
|
||||||
import { extractHeadings, mdxRenderOptions } from "@/utils";
|
import { extractExcerpt, extractHeadings, mdxRenderOptions } from "@/utils";
|
||||||
import { Box, Divider, Typography, Link, Chip } from "@mui/material";
|
import { Box, Divider, Typography, Link, Chip } from "@mui/material";
|
||||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||||
import { findType, interpretType } from "@/models/nix";
|
import { findType, interpretType } from "@/models/nix";
|
||||||
@ -50,10 +50,12 @@ const Toc = async (props: TocProps) => {
|
|||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="subtitle1">On this page</Typography>
|
<Typography variant="subtitle1" component={"div"}>
|
||||||
|
On this page
|
||||||
|
</Typography>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||||
{title && (
|
{title && (
|
||||||
<Link href={`#${title}`}>
|
<Link rel="canonical" href={`#${title}`}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{
|
sx={{
|
||||||
@ -68,7 +70,7 @@ const Toc = async (props: TocProps) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{headings.map((h, idx) => (
|
{headings.map((h, idx) => (
|
||||||
<Link key={idx} href={`#${h.id}`}>
|
<Link rel="canonical" key={idx} href={`#${h.id}`}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{
|
sx={{
|
||||||
@ -171,10 +173,25 @@ export async function generateMetadata(
|
|||||||
const keywords = item?.meta?.path || [];
|
const keywords = item?.meta?.path || [];
|
||||||
const alias_keywords = item?.meta.aliases?.flat() || [];
|
const alias_keywords = item?.meta.aliases?.flat() || [];
|
||||||
|
|
||||||
const title = item?.meta.path[item?.meta.path.length - 1];
|
// words?.forEach((word) => {
|
||||||
|
// console.log({ word });
|
||||||
|
// });
|
||||||
|
// console.log({ excerpt });
|
||||||
|
|
||||||
|
const name = item?.meta.path[item?.meta.path.length - 1];
|
||||||
|
|
||||||
|
const content = item?.content?.content || "";
|
||||||
|
|
||||||
|
const summary = await extractExcerpt(content, 35);
|
||||||
|
const description = await extractExcerpt(content, 200);
|
||||||
|
|
||||||
|
const title = `Nix ${name} - ${summary}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
// should be 20-70 characters
|
||||||
title,
|
title,
|
||||||
description: item?.content?.content,
|
// should be 70-160 characters
|
||||||
|
description,
|
||||||
authors: [
|
authors: [
|
||||||
{ name: "nixos", url: "https://nixos.org/" },
|
{ name: "nixos", url: "https://nixos.org/" },
|
||||||
{ name: "nixpkgs", url: "https://github.com/NixOS/nixpkgs" },
|
{ name: "nixpkgs", url: "https://github.com/NixOS/nixpkgs" },
|
||||||
@ -233,7 +250,7 @@ export default async function Page(props: { params: { path: string[] } }) {
|
|||||||
idx === all.length - 1 ? (
|
idx === all.length - 1 ? (
|
||||||
<React.Fragment key={idx}>
|
<React.Fragment key={idx}>
|
||||||
<meta data-pagefind-meta={`name:${attr}`} />
|
<meta data-pagefind-meta={`name:${attr}`} />
|
||||||
<Box component="h3" sx={{ display: "none" }}>
|
<Box component="h2" sx={{ display: "none" }}>
|
||||||
{attr}
|
{attr}
|
||||||
</Box>
|
</Box>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -348,7 +365,7 @@ export default async function Page(props: { params: { path: string[] } }) {
|
|||||||
<Divider flexItem />
|
<Divider flexItem />
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
component={"h3"}
|
component={"h4"}
|
||||||
sx={{
|
sx={{
|
||||||
color: "text.secondary",
|
color: "text.secondary",
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
@ -366,14 +383,16 @@ export default async function Page(props: { params: { path: string[] } }) {
|
|||||||
This is a Functor
|
This is a Functor
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<Link href="/md/tutorials/functors">Learn about functors</Link>
|
<Link rel="canonical" href="/md/tutorials/functors">
|
||||||
|
Learn about functors
|
||||||
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!!meta?.aliases?.length && (
|
{!!meta?.aliases?.length && (
|
||||||
<>
|
<>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h5"
|
variant="h5"
|
||||||
component={"div"}
|
component={"h5"}
|
||||||
sx={{ color: "text.secondary" }}
|
sx={{ color: "text.secondary" }}
|
||||||
>
|
>
|
||||||
Aliases
|
Aliases
|
||||||
@ -381,7 +400,9 @@ export default async function Page(props: { params: { path: string[] } }) {
|
|||||||
<ul>
|
<ul>
|
||||||
{meta?.aliases?.map((a) => (
|
{meta?.aliases?.map((a) => (
|
||||||
<li key={a.join(".")}>
|
<li key={a.join(".")}>
|
||||||
<Link href={`/f/${a.join("/")}`}>{a.join(".")}</Link>
|
<Link rel="canonical" href={`/f/${a.join("/")}`}>
|
||||||
|
{a.join(".")}
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -6,7 +6,7 @@ import { ClientSideLayoutContext } from "@/components/ClientSideLayoutContext";
|
|||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Noogle",
|
title: "Noogle - Simply find Nix API reference documentation.",
|
||||||
description:
|
description:
|
||||||
"Nix API reference. Includes nix, nixpkgs and nixos. Search nix functions within the nix ecosystem based on type, name, description, example, category and more.",
|
"Nix API reference. Includes nix, nixpkgs and nixos. Search nix functions within the nix ecosystem based on type, name, description, example, category and more.",
|
||||||
creator: "@hsjobeki",
|
creator: "@hsjobeki",
|
||||||
|
@ -24,7 +24,7 @@ export default function Home() {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link href="/md/release/2024-1" underline="none">
|
<Link rel="canonical" href="/md/release/2024-1" underline="none">
|
||||||
<Tooltip title="Whats new">
|
<Tooltip title="Whats new">
|
||||||
<Typography
|
<Typography
|
||||||
variant="h1"
|
variant="h1"
|
||||||
|
@ -19,7 +19,7 @@ export const ListGroup = async (props: ListGroupProps) => {
|
|||||||
const matter = await getMdxMeta(entry);
|
const matter = await getMdxMeta(entry);
|
||||||
const { frontmatter } = matter.compiled;
|
const { frontmatter } = matter.compiled;
|
||||||
return (
|
return (
|
||||||
<Link key={`${idx}`} href={`/ref/${entry.join("/")}`}>
|
<Link rel="canonical" key={`${idx}`} href={`/ref/${entry.join("/")}`}>
|
||||||
<ListItem
|
<ListItem
|
||||||
disablePadding
|
disablePadding
|
||||||
disableGutters
|
disableGutters
|
||||||
|
@ -284,7 +284,11 @@ export function PagefindResults() {
|
|||||||
secondaryTypographyProps={{
|
secondaryTypographyProps={{
|
||||||
variant: "body1",
|
variant: "body1",
|
||||||
}}
|
}}
|
||||||
primary={<Link href={`${url}`}>{meta.title}</Link>}
|
primary={
|
||||||
|
<Link rel="canonical" href={`${url}`}>
|
||||||
|
{meta.title}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
secondary={
|
secondary={
|
||||||
<div dangerouslySetInnerHTML={{ __html: excerpt }} />
|
<div dangerouslySetInnerHTML={{ __html: excerpt }} />
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ export const PositionLink = ({
|
|||||||
<>
|
<>
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
|
component={"div"}
|
||||||
sx={{ color: "text.secondary", pb: 2 }}
|
sx={{ color: "text.secondary", pb: 2 }}
|
||||||
>
|
>
|
||||||
This function is not declared in a .nix file
|
This function is not declared in a .nix file
|
||||||
@ -56,6 +57,7 @@ export const PositionLink = ({
|
|||||||
{!is_primop && (
|
{!is_primop && (
|
||||||
<Typography
|
<Typography
|
||||||
variant="subtitle2"
|
variant="subtitle2"
|
||||||
|
component={"div"}
|
||||||
sx={{ color: "text.secondary", pb: 2 }}
|
sx={{ color: "text.secondary", pb: 2 }}
|
||||||
>
|
>
|
||||||
This is very likely a bug in noogle please report this error.
|
This is very likely a bug in noogle please report this error.
|
||||||
@ -143,7 +145,11 @@ export const PositionLink = ({
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle2" sx={{ color: "text.secondary" }}>
|
<Typography
|
||||||
|
variant="subtitle2"
|
||||||
|
component={"div"}
|
||||||
|
sx={{ color: "text.secondary" }}
|
||||||
|
>
|
||||||
{contentPosition &&
|
{contentPosition &&
|
||||||
is_inherited &&
|
is_inherited &&
|
||||||
`(${content_meta?.path?.join(".")})`}
|
`(${content_meta?.path?.join(".")})`}
|
||||||
@ -151,7 +157,7 @@ export const PositionLink = ({
|
|||||||
</Box>
|
</Box>
|
||||||
{!content?.content && (
|
{!content?.content && (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h5" sx={{ pt: 2 }}>
|
<Typography variant="h5" component={"div"} sx={{ pt: 2 }}>
|
||||||
{"Contribute"}
|
{"Contribute"}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
@ -184,7 +190,6 @@ export const PositionLink = ({
|
|||||||
secondary="Contribute to Noogle"
|
secondary="Contribute to Noogle"
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
{/* </Link> */}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
|
@ -90,6 +90,7 @@ export const Navigation = ({
|
|||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
{prev && (
|
{prev && (
|
||||||
<Link
|
<Link
|
||||||
|
rel="canonical"
|
||||||
href={prev.url}
|
href={prev.url}
|
||||||
sx={{
|
sx={{
|
||||||
my: 2,
|
my: 2,
|
||||||
@ -111,6 +112,7 @@ export const Navigation = ({
|
|||||||
|
|
||||||
{next && (
|
{next && (
|
||||||
<Link
|
<Link
|
||||||
|
rel="canonical"
|
||||||
href={next.url}
|
href={next.url}
|
||||||
sx={{
|
sx={{
|
||||||
ml: "auto",
|
ml: "auto",
|
||||||
|
@ -116,7 +116,10 @@ export function SearchResults() {
|
|||||||
variant: "body1",
|
variant: "body1",
|
||||||
}}
|
}}
|
||||||
primary={
|
primary={
|
||||||
<Link href={`f/${meta.path.join("/")}`}>
|
<Link
|
||||||
|
rel="canonical"
|
||||||
|
href={`f/${meta.path.join("/")}`}
|
||||||
|
>
|
||||||
{meta.title}
|
{meta.title}
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,11 @@ export const Filter = (props: FilterProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", pr: 1 }}>
|
<Box sx={{ display: "flex", alignItems: "center", pr: 1 }}>
|
||||||
<Typography variant="subtitle2" sx={{ color: "text.secondary" }}>
|
<Typography
|
||||||
|
variant="subtitle2"
|
||||||
|
component="div"
|
||||||
|
sx={{ color: "text.secondary" }}
|
||||||
|
>
|
||||||
From
|
From
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -184,7 +188,11 @@ export const Filter = (props: FilterProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", pr: 1 }}>
|
<Box sx={{ display: "flex", alignItems: "center", pr: 1 }}>
|
||||||
<Typography variant="subtitle2" sx={{ color: "text.secondary" }}>
|
<Typography
|
||||||
|
variant="subtitle2"
|
||||||
|
component="div"
|
||||||
|
sx={{ color: "text.secondary" }}
|
||||||
|
>
|
||||||
To
|
To
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -46,6 +46,7 @@ export const Header = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
|
rel="canonical"
|
||||||
href="/"
|
href="/"
|
||||||
className={fira.className}
|
className={fira.className}
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -34,7 +34,7 @@ export const Preview = (props: PreviewProps) => {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link href={`/f/${meta.path.join("/")}`}>
|
<Link rel="canonical" href={`/f/${meta.path.join("/")}`}>
|
||||||
<Typography
|
<Typography
|
||||||
component="h3"
|
component="h3"
|
||||||
variant="h4"
|
variant="h4"
|
||||||
|
@ -17,6 +17,7 @@ import remarkRehype from "remark-rehype";
|
|||||||
import remarkUnlink from "remark-unlink";
|
import remarkUnlink from "remark-unlink";
|
||||||
|
|
||||||
import { unified } from "unified";
|
import { unified } from "unified";
|
||||||
|
import { rehypeExtractExcerpt } from "./excerpt";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to generate a set from a path in lodash style
|
* Function to generate a set from a path in lodash style
|
||||||
@ -112,6 +113,23 @@ type Heading = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const extractExcerpt = async (
|
||||||
|
content: string,
|
||||||
|
maxLength: number
|
||||||
|
): Promise<string> => {
|
||||||
|
const processor = unified()
|
||||||
|
.use(remarkParse)
|
||||||
|
.use(remarkRehype)
|
||||||
|
.use(rehypeExtractExcerpt, {
|
||||||
|
maxLength: maxLength,
|
||||||
|
ellipsis: " ...",
|
||||||
|
})
|
||||||
|
.use(rehypeStringify);
|
||||||
|
|
||||||
|
const result = await processor.process(content);
|
||||||
|
|
||||||
|
return result.data.excerpt as string;
|
||||||
|
};
|
||||||
export const extractHeadings = async (content: string): Promise<Heading[]> => {
|
export const extractHeadings = async (content: string): Promise<Heading[]> => {
|
||||||
const processor = unified()
|
const processor = unified()
|
||||||
.use(remarkParse)
|
.use(remarkParse)
|
||||||
|
Loading…
Reference in New Issue
Block a user