_includes/layouts/BaseLayout.11ty.tsx (163 lines of code) (raw):

import Navbar from "../navbar/Navbar.11ty"; import Footer from "../footer/Footer.11ty"; import { LayoutContext, LayoutProps } from "../../src/models"; import { MetaOpenGraphImage } from "../../src/plugins/metaOpenGraphImagePlugin"; import { Resource } from "../../src/ResourceModels"; import { GoogleTagManagerBodyNoScript, GoogleTagManagerHeadScript, } from "../googleTagManager.11ty"; import Subnav from "../navbar/Subnav.11ty"; import PromoBanner from "../navbar/PromoBanner.11ty"; import { Channel, isChannel } from "../resources/channel/ChannelModels"; import { Fragment } from "jsx-async-runtime/jsx-dev-runtime"; import { isLink } from "../resources/link/LinkModels"; export type BaseLayoutProps = { title: string; subtitle?: string; video?: | string | { url: string; start: number; end: number; }; resourceType: string; channel?: string; } & LayoutProps; export function BaseLayout( this: LayoutContext, data: BaseLayoutProps, ): JSX.Element { const { children, title, subtitle, resourceType, collections } = data; // Happy DOM throws a DOMException for external script/css even though // we do the settings to suppress it. Vite catches the exception but // logs it. We can't handle the exception, and it pollutes the test output. // Let's detect if we're running in a test, then later, wrap the // <link> and <script> to suppress. const isNotTest = !( typeof window != "undefined" && !!(window as any).happyDOM ); // determine if there's an og:image let channel: Channel | undefined = undefined; let thumbnail: string | undefined = undefined; let canonicalURL: string | undefined = undefined; if (resourceType) { const resource = collections.resourceMap.get(data.page.url) as Resource; const resourceThumbnail = resource?.getThumbnail(); canonicalURL = resource?.canonical; // make sure we don't try to use fontawesome icons as the thumbnail image if ( resourceThumbnail && resourceThumbnail.indexOf("fa-") < 0 && resourceThumbnail.indexOf("fas-") < 0 && resourceThumbnail.indexOf("far-") < 0 ) { thumbnail = resourceThumbnail; } if (isChannel(resource)) { channel = resource; } else if ( resource && resource.references && resource.references.channel?.url ) { channel = resource.references.channel; } else if (data.channel) { channel = collections.resourceMap.get(data.channel) as Channel; } if (isLink(resource)) { if (canonicalURL) { console.warn( `canonical URL (${canonicalURL}) set on ${resource.url} but is a Link URL. Ignoring.`, ); } // overwrite canonical URL if this is a Link URL canonicalURL = resource?.linkURL; } } const year = new Date().getFullYear(); const copyright = `Copyright © 2000–${year} <a href="https://www.jetbrains.com/">JetBrains</a> s.r.o.`; // TODO: data below should probably be cached and defined elsewhere, but hey - WIP! // data for navbar const featuredChannel = collections.resourceMap.get("/remote/"); const technologies = [ collections.resourceMap.get("/javascript/"), collections.resourceMap.get("/python/"), collections.resourceMap.get("/java/"), collections.resourceMap.get("/go/"), collections.resourceMap.get("/dotnet/"), collections.resourceMap.get("/kotlin/"), ].filter((it) => it != undefined) as Resource[]; const solutions = [ collections.resourceMap.get("/gamedev/"), collections.resourceMap.get("/ai/"), collections.resourceMap.get("/remote/"), collections.resourceMap.get("/databases/"), collections.resourceMap.get("/django/"), ].filter((it) => it != undefined) as Resource[]; const hotTopics = [ collections.resourceMap.get("topics:aws"), collections.resourceMap.get("topics:debugging"), collections.resourceMap.get("topics:git"), collections.resourceMap.get("topics:gcp"), collections.resourceMap.get("topics:gradle"), collections.resourceMap.get("topics:refactoring"), ].filter((it) => it != undefined) as Resource[]; // render return ( <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{data.title} - JetBrains Guide</title> <link rel="preload" as="font" href="https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans.woff2" crossorigin="anonymous" /> <link rel="preload" as="font" href="https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-Regular.woff2" crossorigin="anonymous" /> <link rel="preload" as="style" href="/assets/site.scss" /> {isNotTest && ( <Fragment> <link rel="stylesheet" href="/assets/site.scss" /> <script defer src="/assets/js/site.js" type="module"></script> <script defer src="/assets/js/video.js" type="module"></script> </Fragment> )} {canonicalURL && <link rel="canonical" href={canonicalURL} />} <link rel="icon" href="/assets/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="/assets/favicon.ico" /> <link rel="alternate" type="application/rss+xml" title={`${title} RSS Feed`} href={`/rss.xml`} /> <meta property="og:title" content={title} /> {subtitle && <meta property="og:description" content={subtitle} />} <meta property="og:type" content="article" /> <meta property="article:published_time" content="2023-02-17" /> <meta property="article:author" content="" /> <meta property="article:section" content="" /> {thumbnail && <meta property="og:image:alt" content={title} />} <meta name="twitter:card" content="summary" /> <meta name="twitter:site" content="@jetbrains" /> {subtitle && <meta name="description" content={subtitle} />} <GoogleTagManagerHeadScript googleTagManagerId="GTM-5P98" /> </head> <body> <GoogleTagManagerBodyNoScript googleTagManagerId="GTM-5P98" /> <Navbar featuredResource={featuredChannel} technologies={technologies} solutions={solutions} topics={hotTopics} /> {channel && <Subnav channel={channel} />} {channel && <PromoBanner channel={channel} />} {children} <Footer copyright={copyright}></Footer> {thumbnail && ( <MetaOpenGraphImage siteUrl="https://www.jetbrains.com/guide/" src={thumbnail} /> )} </body> </html> ); } export const render = BaseLayout;