1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 04:25:22 -04:00

enable vercel analytics

This commit is contained in:
Jake Jarvis 2023-06-17 20:14:43 -04:00
parent cab79559e6
commit afbdcc7b06
Signed by: jake
GPG Key ID: 2B0C9CF251E69A39
23 changed files with 3046 additions and 2049 deletions

View File

@ -89,12 +89,13 @@ const CornerCopyButton = styled(CopyButton, {
export type CodeBlockProps = ComponentProps<typeof Code> & {
highlight?: boolean;
withCopyButton?: boolean;
};
const CodeBlock = ({ highlight, className, children, ...rest }: CodeBlockProps) => {
const CodeBlock = ({ highlight, withCopyButton, className, children, ...rest }: CodeBlockProps) => {
return (
<Block highlight={highlight}>
<CornerCopyButton source={children} />
{withCopyButton && <CornerCopyButton source={children} />}
<Code className={className?.replace("code-highlight", "").trim()} {...rest}>
{children}
</Code>

View File

@ -20,6 +20,7 @@ const CodeHybrid = ({ forceBlock, className, children, ...rest }: CodeHybridProp
return (
<CodeBlock
highlight={prismEnabled && !classNames?.includes("language-plaintext")}
withCopyButton
className={className}
{...rest}
>

View File

@ -3,10 +3,11 @@
// directory containing .mdx files relative to project root
export const NOTES_DIR = "notes";
// normalize the timestamp saved when building/deploying (see next.config.js) and fall back to right now:
// normalize the timestamp saved when building/deploying (see next.config.js) and fall back to right now
export const RELEASE_DATE = new Date(process.env.RELEASE_DATE || Date.now()).toISOString();
// detect if running locally via `next dev` (phase is checked in next.config.js)
export const IS_DEV_SERVER = process.env.IS_DEV_SERVER === "true";
// attempt to normalize the various environment flags
export const BUILD_ENV = process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.NODE_ENV;

View File

@ -1,8 +1,8 @@
import type { DefaultSeoProps, SocialProfileJsonLdProps, ArticleJsonLdProps } from "next-seo";
import * as config from ".";
import { meJpg, faviconPng, faviconIco, appleTouchIconPng } from "./favicons";
import type { DefaultSeoProps, SocialProfileJsonLdProps, ArticleJsonLdProps } from "next-seo";
// Most of this file simply takes the data already defined in ./config.js and translates it into objects that are
// compatible with next-seo's props:
// https://github.com/garmeeh/next-seo#default-seo-configuration
@ -49,6 +49,7 @@ export const defaultSeo: DefaultSeoProps = {
{
rel: "icon",
href: faviconIco.src,
sizes: "any", // https://twitter.com/subzey/status/1417099064949235712
},
{
rel: "icon",

View File

@ -1,11 +1,13 @@
import fs from "fs/promises";
import path from "path";
import glob from "fast-glob";
import matter from "gray-matter";
import { marked } from "marked";
import removeMarkdown from "remove-markdown";
import pMap from "p-map";
import pMemoize from "p-memoize";
import matter from "gray-matter";
import removeMarkdown from "remove-markdown";
import { marked } from "marked";
// @ts-ignore
import { markedSmartypants } from "marked-smartypants";
import { formatDate } from "./format-date";
import { baseUrl } from "../config";
import { NOTES_DIR } from "../config/constants";
@ -36,6 +38,10 @@ export const getNoteData = async (
const rawContent = await fs.readFile(fullPath, "utf8");
const { data, content } = matter(rawContent);
// attach marked extensions:
// https://marked.js.org/using_advanced#extensions
marked.use(markedSmartypants());
// return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content
return {
frontMatter: {
@ -45,7 +51,9 @@ export const getNoteData = async (
// allow markdown formatting to appear in post titles in some places (rarely used):
htmlTitle: marked.parseInline(data.title, {
silent: true,
smartypants: true,
// these are deprecated and throw very noisy warnings but are still defaults, make it make sense...
mangle: false,
headerIds: false,
}),
slug,
permalink: `${baseUrl}/${NOTES_DIR}/${slug}/`,

View File

@ -0,0 +1,6 @@
// a weird system but makes it impossible to accidentally end up with multiple imports of the same font. see:
// https://nextjs.org/docs/pages/building-your-application/optimizing/fonts#reusing-fonts
export { default as Inter } from "./loaders/Inter";
export { default as SourceCodePro } from "./loaders/SourceCodePro";
export { default as ComicNeue } from "./loaders/ComicNeue";

View File

@ -0,0 +1,13 @@
import { Comic_Neue as ComicNeueLoader } from "next/font/google";
const ComicNeue = ComicNeueLoader({
weight: ["400", "700"],
style: ["normal", "italic"],
subsets: ["latin"],
display: "swap",
fallback: ["'Comic Sans MS'", "'Comic Sans'"],
adjustFontFallback: false,
preload: false,
});
export default ComicNeue;

View File

@ -0,0 +1,10 @@
import { Inter as InterLoader } from "next/font/google";
const Inter = InterLoader({
weight: "variable",
subsets: ["latin"],
display: "fallback",
preload: true,
});
export default Inter;

View File

@ -0,0 +1,10 @@
import { Source_Code_Pro as SourceCodeProLoader } from "next/font/google";
const SourceCodePro = SourceCodeProLoader({
weight: "variable",
subsets: ["latin"],
display: "fallback",
preload: true,
});
export default SourceCodePro;

View File

@ -2,11 +2,11 @@ import { createStitches } from "@stitches/react";
import type * as Stitches from "@stitches/react";
// misc. helpers
import hexToRgba from "./utils/hex-to-rgba";
import normalizeStyles from "./utils/normalize";
import { rgba } from "polished";
import normalizeCss from "stitches-normalize";
// web fonts
import { Inter, SourceCodePro } from "./utils/fonts";
import { Inter, SourceCodePro } from "./fonts";
// https://stitches.dev/docs/typescript#type-a-css-object
export type CSS = Stitches.CSS<typeof stitchesConfig>;
@ -30,7 +30,7 @@ export const {
colors: {
backgroundInner: "#ffffff",
backgroundOuter: "#fcfcfc",
backgroundHeader: hexToRgba("#fcfcfc", 0.7),
backgroundHeader: rgba("#fcfcfc", 0.7),
text: "#202020",
mediumDark: "#515151",
medium: "#5e5e5e",
@ -40,7 +40,7 @@ export const {
superLight: "#f4f4f4",
superDuperLight: "#fbfbfb",
link: "#0e6dc2",
linkUnderline: hexToRgba("#0e6dc2", 0.4),
linkUnderline: rgba("#0e6dc2", 0.4),
success: "#44a248",
error: "#ff1b1b",
warning: "#f78200",
@ -93,7 +93,7 @@ export const {
alpha?: number;
}) => ({
// allow both pre-set rgba stitches variables and hex values
$$underlineColor: color.startsWith("#") ? hexToRgba(color, alpha) : color,
$$underlineColor: color.startsWith("#") ? rgba(color, alpha) : color,
}),
},
});
@ -102,7 +102,7 @@ export const darkTheme = createTheme({
colors: {
backgroundInner: "#1e1e1e",
backgroundOuter: "#252525",
backgroundHeader: hexToRgba("#252525", 0.85),
backgroundHeader: rgba("#252525", 0.85),
text: "#f1f1f1",
mediumDark: "#d7d7d7",
medium: "#b1b1b1",
@ -112,7 +112,7 @@ export const darkTheme = createTheme({
superLight: "#272727",
superDuperLight: "#1f1f1f",
link: "#88c7ff",
linkUnderline: hexToRgba("#88c7ff", 0.4),
linkUnderline: rgba("#88c7ff", 0.4),
success: "#78df55",
error: "#ff5151",
warning: "#f2b702",
@ -132,9 +132,12 @@ export const darkTheme = createTheme({
},
});
// @ts-ignore
export const globalStyles = globalCss(
// @ts-ignore
normalizeStyles,
...normalizeCss({
systemFonts: false,
}),
{
body: {
fontFamily: theme.fonts.sans,

View File

@ -1,32 +0,0 @@
import {
Inter as InterLoader,
Source_Code_Pro as SourceCodeProLoader,
Comic_Neue as ComicNeueLoader,
} from "next/font/google";
const Inter = InterLoader({
weight: "variable",
subsets: ["latin"],
display: "fallback",
preload: true,
});
const SourceCodePro = SourceCodeProLoader({
weight: "variable",
subsets: ["latin"],
display: "fallback",
preload: true,
});
// only for use in pages/previously.tsx (and tree-shaken out everywhere else in production)
const ComicNeue = ComicNeueLoader({
weight: ["400", "700"],
style: ["normal", "italic"],
subsets: ["latin"],
display: "swap",
fallback: ["'Comic Sans MS'", "'Comic Sans'"],
adjustFontFallback: false,
preload: false,
});
export { Inter, SourceCodePro, ComicNeue };

View File

@ -1,6 +0,0 @@
import hexToRgbaOrig from "hex-to-rgba";
// removes spaces from default hex-to-rgba output
const hexToRgba = (color: string, alpha?: number) => hexToRgbaOrig(color, alpha).replace(/\s/g, "");
export default hexToRgba;

View File

@ -1,104 +0,0 @@
/*! stitches-normalize | MIT License | https://github.com/jakejarvis/stitches-normalize */
/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
import type * as Stitches from "@stitches/react";
const normalizeStyles: Record<string, Stitches.CSSProperties> = {
"*, ::before, ::after": {
boxSizing: "border-box",
},
html: {
lineHeight: 1.15,
tabSize: 4,
// @ts-ignore
WebkitTextSizeAdjust: "100%",
},
body: {
margin: 0,
},
hr: {
height: 0,
color: "inherit",
},
"abbr[title]": {
textDecoration: "underline dotted",
},
"b, strong": {
fontWeight: "bolder",
},
"code, kbd, samp, pre": {
fontSize: "1em",
},
small: {
fontSize: "80%",
},
"sub, sup": {
fontSize: "75%",
lineHeight: 0,
position: "relative",
verticalAlign: "baseline",
},
sub: {
bottom: "-0.25em",
},
sup: {
top: "-0.5em",
},
table: {
textIndent: 0,
borderColor: "inherit",
},
"button, input, optgroup, select, textarea": {
fontFamily: "inherit",
fontSize: "100%",
lineHeight: 1.15,
margin: 0,
// @ts-ignore
WebkitAppearance: "button",
},
"button, select": {
textTransform: "none",
},
legend: {
padding: 0,
},
progress: {
verticalAlign: "baseline",
},
summary: {
display: "list-item",
},
"[type='search']": {
outlineOffset: -2,
// @ts-ignore
WebkitAppearance: "textfield",
},
// `-webkit` compatibility properties and rules
"::-webkit-search-decoration": {
// @ts-ignore
WebkitAppearance: "none",
},
"::-webkit-inner-spin-button, ::-webkit-outer-spin-button": {
height: "auto",
},
"::-webkit-file-upload-button": {
font: "inherit",
// @ts-ignore
WebkitAppearance: "button",
},
// `-moz` compatibility properties and rules
"::-moz-focus-inner": {
borderStyle: "none",
padding: 0,
},
":-moz-focusring": {
outline: "1px dotted ButtonText",
},
":-moz-ui-invalid": {
boxShadow: "none",
},
};
export default normalizeStyles;

View File

@ -4,7 +4,7 @@ const path = require("path");
const { PHASE_DEVELOPMENT_SERVER } = require("next/constants");
const config = require("./lib/config");
module.exports = (phase) => {
module.exports = (/** @type {string} */ phase) => {
/**
* @type {import("next").NextConfig}
*/
@ -13,6 +13,7 @@ module.exports = (phase) => {
reactStrictMode: true,
trailingSlash: true,
productionBrowserSourceMaps: true,
transpilePackages: ["@novnc/novnc"],
env: {
BASE_URL:
process.env.NEXT_PUBLIC_VERCEL_ENV !== "production" && process.env.NEXT_PUBLIC_VERCEL_URL !== undefined

4658
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
},
"sideEffects": false,
"scripts": {
"dev": "cross-env NODE_OPTIONS='--inspect' next dev",
"dev": "next dev -H 0.0.0.0",
"build": "next build",
"start": "next start",
"lint": "eslint . --ext js,jsx,ts,tsx,md,mdx"
@ -19,40 +19,42 @@
"dependencies": {
"@giscus/react": "^2.2.8",
"@hcaptcha/react-hcaptcha": "^1.8.1",
"@novnc/novnc": "github:novnc/novnc#747603c0d5bbdc8ac31b81f7a1b31291a397d280",
"@octokit/graphql": "^5.0.5",
"@octokit/graphql-schema": "^14.1.0",
"@primer/octicons": "^18.3.0",
"@prisma/client": "^4.12.0",
"@novnc/novnc": "git+https://git@github.com/novnc/novnc#e8ad466e45a21598debe81824744d85ff2323c7b",
"@octokit/graphql": "^5.0.6",
"@octokit/graphql-schema": "^14.11.0",
"@primer/octicons": "^19.3.0",
"@prisma/client": "^4.15.0",
"@react-spring/web": "^9.7.2",
"@sentry/node": "^7.47.0",
"@sentry/tracing": "^7.47.0",
"@sentry/node": "^7.55.2",
"@sentry/tracing": "^7.55.2",
"@stitches/react": "^1.2.8",
"@vercel/analytics": "^1.0.1",
"comma-number": "^2.1.0",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.7",
"dayjs": "^1.11.8",
"fast-glob": "^3.2.12",
"fathom-client": "^3.5.0",
"feather-icons": "^4.29.0",
"feed": "^4.2.2",
"formik": "^2.2.9",
"formik": "^2.4.2",
"gray-matter": "^4.0.3",
"hex-to-rgba": "^2.0.1",
"marked": "^4.3.0",
"next": "13.3.0",
"marked": "^5.1.0",
"marked-smartypants": "^1.0.2",
"next": "13.4.6",
"next-mdx-remote": "^4.4.1",
"next-seo": "^5.15.0",
"next-seo": "^6.0.0",
"obj-str": "^1.1.0",
"p-map": "^5.5.0",
"p-map": "^6.0.0",
"p-memoize": "^7.1.1",
"polished": "^4.2.2",
"prop-types": "^15.8.1",
"query-string": "^8.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "^4.0.3",
"react-error-boundary": "^4.0.10",
"react-gist": "^1.2.4",
"react-innertext": "^1.1.5",
"react-intersection-observer": "^9.4.3",
"react-intersection-observer": "^9.5.0",
"react-is": "18.2.0",
"react-player": "~2.10.1",
"react-textarea-autosize": "^8.4.1",
@ -63,40 +65,41 @@
"remark-smartypants": "^2.0.0",
"remark-unwrap-images": "^3.0.1",
"remove-markdown": "^0.5.0",
"simple-icons": "^8.10.0",
"simple-icons": "^9.1.0",
"sitemap": "^7.1.1",
"swr": "^2.1.3"
"stitches-normalize": "^2.0.0",
"swr": "^2.1.5"
},
"devDependencies": {
"@jakejarvis/eslint-config": "*",
"@svgr/webpack": "^7.0.0",
"@svgr/webpack": "^8.0.1",
"@types/comma-number": "^2.1.0",
"@types/marked": "^4.0.8",
"@types/marked": "^5.0.0",
"@types/node": "*",
"@types/novnc__novnc": "^1.3.0",
"@types/prop-types": "^15.7.5",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@types/react-is": "^17.0.3",
"@types/react": "^18.2.12",
"@types/react-dom": "^18.2.5",
"@types/react-is": "^18.2.0",
"@types/remove-markdown": "^0.3.1",
"@types/uglify-js": "^3.17.1",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
"cross-env": "^7.0.3",
"eslint": "~8.38.0",
"eslint-config-next": "13.3.0",
"eslint": "~8.43.0",
"eslint-config-next": "13.4.6",
"eslint-config-prettier": "~8.8.0",
"eslint-plugin-mdx": "~2.0.5",
"eslint-plugin-mdx": "~2.1.0",
"eslint-plugin-prettier": "~4.2.1",
"lint-staged": "^13.2.1",
"prettier": "^2.8.7",
"prisma": "^4.12.0",
"lint-staged": "^13.2.2",
"prettier": "^2.8.8",
"prisma": "^4.15.0",
"simple-git-hooks": "^2.8.1",
"typescript": "^5.0.4",
"typescript": "^5.1.3",
"uglify-js": "^3.17.4"
},
"optionalDependencies": {
"sharp": "^0.32.0"
"sharp": "^0.32.1"
},
"engines": {
"node": ">=16.x"
@ -113,9 +116,9 @@
"eslint"
]
},
"packageManager": "npm@9.6.4",
"packageManager": "npm@9.7.1",
"volta": {
"node": "18.16.0",
"npm": "9.6.4"
"npm": "9.7.1"
}
}

View File

@ -1,6 +1,7 @@
import { useEffect } from "react";
import { useRouter } from "next/router";
import { DefaultSeo, SocialProfileJsonLd } from "next-seo";
import { Analytics } from "@vercel/analytics/react";
import * as Fathom from "fathom-client";
import { ThemeProvider } from "../contexts/ThemeContext";
import Layout from "../components/Layout";
@ -73,6 +74,8 @@ const App = ({ Component, pageProps }: AppProps) => {
<SocialProfileJsonLd {...socialProfileJsonLd} />
<ThemeProvider classNames={themeClassNames}>{getLayout(<Component {...pageProps} />)}</ThemeProvider>
<Analytics />
</>
);
};

View File

@ -32,7 +32,7 @@ const CLI = () => {
<Image src={cliImg} href="https://www.npmjs.com/package/@jakejarvis/cli" alt="Terminal Screenshot" priority />
<H2 id="usage">Usage</H2>
<CodeBlock>npx @jakejarvis/cli</CodeBlock>
<CodeBlock withCopyButton>npx @jakejarvis/cli</CodeBlock>
<H2 id="inspired-by">Inspired by</H2>
<UnorderedList>

View File

@ -9,7 +9,7 @@ import CodeInline from "../components/CodeInline";
import HorizontalRule from "../components/HorizontalRule";
import { Windows95Logo } from "../components/Icons";
import { styled, theme } from "../lib/styles/stitches.config";
import { ComicNeue } from "../lib/styles/utils/fonts";
import { ComicNeue } from "../lib/styles/fonts";
import type { ReactElement } from "react";
import img_wayback from "../public/static/images/previously/wayback.png";

32
pages/robots.txt.ts Normal file
View File

@ -0,0 +1,32 @@
import { baseUrl } from "../lib/config";
import type { GetServerSideProps } from "next";
export const getServerSideProps: GetServerSideProps<Record<string, never>> = async (context) => {
const { res } = context;
// this production check should be unnecessary because "noindex" and "nofollow" are also set in a meta tag (see
// DefaultSeo's props in pages/_app.tsx), but it doesn't hurt...
const robots = `User-agent: *
${
process.env.NEXT_PUBLIC_VERCEL_ENV !== "production"
? `Disallow: /`
: `Allow: /
Sitemap: ${baseUrl}/sitemap.xml`
}
`;
res.setHeader("content-type", "text/plain; charset=utf-8");
// cache on edge for one week
res.setHeader("cache-control", "public, max-age=0, s-maxage=604800, stale-while-revalidate");
res.write(robots);
res.end();
return {
props: {},
};
};
// eslint-disable-next-line import/no-anonymous-default-export
export default () => null;

View File

@ -666,8 +666,8 @@ const Uses = () => {
<strong>Tailscale</strong>
</Link>{" "}
and{" "}
<Link href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/">
<strong>Cloudflare Tunnel</strong>
<Link href="https://developers.cloudflare.com/cloudflare-one/">
<strong>Cloudflare Zero Trust</strong>
</Link>{" "}
to access my home network and VPSes from anywhere.
</ListItem>
@ -678,38 +678,65 @@ const Uses = () => {
</H2>
<UnorderedList>
<ListItem>
<Link href="https://www.tp-link.com/us/home-networking/wifi-router/archer-ax90/">
<strong>TP-Link Archer AX90</strong>
<Link href="https://store.ui.com/us/en/collections/unifi-dream-router/products/udr">
<strong>UniFi Dream Router</strong>
</Link>
, plus:
<UnorderedList>
<ListItem>
2x{" "}
<Link href="https://store.ui.com/us/en/collections/unifi-switching-utility-mini/products/usw-flex-mini">
Switch Flex Mini
</Link>
</ListItem>
<ListItem>
<Link href="https://store.ui.com/us/en/products/unifi-smart-power">SmartPower Plug</Link>{" "}
<em>
(<Link href="https://www.youtube.com/watch?v=iW1tHr4Y_cI">It's Comcastic!</Link>)
</em>
</ListItem>
</UnorderedList>
</ListItem>
<ListItem>
<Link href="https://www.amazon.com/dp/B00HWML468/">
<strong>Dell Inspiron 3647</strong>
<strong>An overpowered custom homelab server</strong>, powered by an{" "}
<Link href="https://www.asus.com/commercial-motherboard/q87me/">ASUS Q87M-E</Link> board,{" "}
<Link href="https://www.intel.com/content/www/us/en/products/sku/80808/intel-core-i74790s-processor-8m-cache-up-to-4-00-ghz/specifications.html">
i7-4790S
</Link>
, upgraded to a Core i7-4790S and 16 GB of memory to dress it up as a <em>really</em> crappy{" "}
<Link href="https://www.vmware.com/products/esxi-and-esx.html">
<strong>VMware ESXi</strong>
, 32 GB of RAM, 3x recertified{" "}
<Link href="https://www.westerndigital.com/products/internal-drives/data-center-drives/ultrastar-dc-hc550-hdd#0F38462">
16TB WD Ultrastar
</Link>{" "}
server, running a few Ubuntu VMs with:
drives, and Ubuntu Server 22.04, in a cheap{" "}
<Link href="https://www.thermaltakeusa.com/versa-h22.html">Thermaltake Versa H22</Link> case with expensive{" "}
<Link href="https://noctua.at/en/nf-a12x25-pwm">Noctua 🤎</Link> fans. Used mainly for local file sharing
via Samba and running{" "}
<Link href="https://github.com/jakejarvis/dotfiles/tree/main/lab">a few Docker containers</Link>, including:
<UnorderedList>
<ListItem>
<Link href="https://www.plex.tv/">Plex</Link>
</ListItem>
<ListItem>
<Link href="https://sonarr.tv/">Sonarr</Link>{" "}
<Link href="https://sonarr.tv/">Sonarr</Link>, <Link href="https://radarr.video/">Radarr</Link>,{" "}
<Link href="https://www.bazarr.media/">Bazarr</Link>,{" "}
<Link href="https://github.com/Prowlarr/Prowlarr">Prowlarr</Link>
</ListItem>
<ListItem>
<Link href="https://radarr.video/">Radarr</Link>
<Link href="https://www.qbittorrent.org/">qBittorrent</Link> (web client)
</ListItem>
<ListItem>
<Link href="https://transmissionbt.com/">Transmission</Link> (via web client)
<Link href="https://tautulli.com/">Tautulli</Link>
</ListItem>
<ListItem>
<Link href="https://homebridge.io/">Homebridge</Link>
<Link href="https://www.home-assistant.io/">Home Assistant</Link>
</ListItem>
<ListItem>
<Link href="https://www.wireguard.com/">WireGuard</Link>
</ListItem>
<ListItem>
<Link href="https://github.com/cloudflare/cloudflared">Cloudflare Tunnel</Link>
</ListItem>
<ListItem>Full post with more details coming soon!</ListItem>
</UnorderedList>
</ListItem>
<ListItem>
@ -722,15 +749,14 @@ const Uses = () => {
2x{" "}
<Link href="https://www.ecobee.com/en-us/smart-thermostats/smart-wifi-thermostat/">
<strong>ecobee3 lite</strong>
</Link>{" "}
smart thermostats (HomeKit support was a must.)
</Link>
</ListItem>
<ListItem>
2x{" "}
<Link href="https://www.sonos.com/en-us/shop/one.html">
<strong>Sonos One</strong>
</Link>{" "}
(with Alexa turned off...allegedly.)
(with Alexa turned off...hopefully? 🤫)
</ListItem>
<ListItem>
2x{" "}

View File

@ -1,4 +0,0 @@
User-Agent: *
Allow: /
Sitemap: https://jarv.is/sitemap.xml

View File

@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -13,8 +17,20 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}