mirror of
https://github.com/d0zingcat/nextjs-notion-starter-kit.git
synced 2026-05-13 15:09:47 +00:00
fix: locally linked
This commit is contained in:
@@ -1,27 +1,17 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"parser": "@typescript-eslint/parser",
|
"extends": ["@fisch0920/eslint-config"],
|
||||||
"plugins": ["@typescript-eslint", "react", "react-hooks"],
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:react/recommended",
|
|
||||||
"plugin:react-hooks/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-explicit-any": 0,
|
"react/prop-types": "off",
|
||||||
"@typescript-eslint/no-non-null-assertion": 0,
|
"unicorn/no-array-reduce": "off",
|
||||||
"@typescript-eslint/no-unused-vars": 2,
|
"unicorn/filename-case": "off",
|
||||||
"react/prop-types": 0
|
"no-process-env": "off",
|
||||||
|
"array-callback-return": "off",
|
||||||
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
|
"jsx-a11y/no-static-element-interactions": "off",
|
||||||
|
"jsx-a11y/media-has-caption": "off",
|
||||||
|
"jsx-a11y/interactive-supports-focus": "off",
|
||||||
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
|
"@typescript-eslint/naming-convention": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
.prettierrc
11
.prettierrc
@@ -7,14 +7,5 @@
|
|||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"bracketSameLine": false,
|
"bracketSameLine": false,
|
||||||
"arrowParens": "always",
|
"arrowParens": "always",
|
||||||
"trailingComma": "none",
|
"trailingComma": "none"
|
||||||
"importOrder": [
|
|
||||||
"^(react/(.*)$)|^(react$)|^(next/(.*)$)|^(next$)",
|
|
||||||
"<THIRD_PARTY_MODULES>",
|
|
||||||
"^(@/lib/(.*)$)|^(@/components/(.*)$)|^(@/styles/(.*)$)",
|
|
||||||
"^[./]"
|
|
||||||
],
|
|
||||||
"importOrderSeparation": true,
|
|
||||||
"importOrderSortSpecifiers": true,
|
|
||||||
"importOrderGroupNamespaceSpecifiers": true
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -8,7 +8,6 @@
|
|||||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/next",
|
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/next",
|
||||||
"runtimeArgs": ["dev"],
|
"runtimeArgs": ["dev"],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"port": 9229,
|
|
||||||
"smartStep": true,
|
"smartStep": true,
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import { PageHead } from './PageHead'
|
import { PageHead } from './PageHead'
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
export const ErrorPage: React.FC<{ statusCode: number }> = ({ statusCode }) => {
|
export function ErrorPage({ statusCode }: { statusCode: number }) {
|
||||||
const title = 'Error'
|
const title = 'Error'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import { FaEnvelopeOpenText } from '@react-icons/all-files/fa/FaEnvelopeOpenText'
|
import { FaEnvelopeOpenText } from '@react-icons/all-files/fa/FaEnvelopeOpenText'
|
||||||
import { FaGithub } from '@react-icons/all-files/fa/FaGithub'
|
import { FaGithub } from '@react-icons/all-files/fa/FaGithub'
|
||||||
import { FaLinkedin } from '@react-icons/all-files/fa/FaLinkedin'
|
import { FaLinkedin } from '@react-icons/all-files/fa/FaLinkedin'
|
||||||
@@ -9,6 +7,7 @@ import { FaYoutube } from '@react-icons/all-files/fa/FaYoutube'
|
|||||||
import { FaZhihu } from '@react-icons/all-files/fa/FaZhihu'
|
import { FaZhihu } from '@react-icons/all-files/fa/FaZhihu'
|
||||||
import { IoMoonSharp } from '@react-icons/all-files/io5/IoMoonSharp'
|
import { IoMoonSharp } from '@react-icons/all-files/io5/IoMoonSharp'
|
||||||
import { IoSunnyOutline } from '@react-icons/all-files/io5/IoSunnyOutline'
|
import { IoSunnyOutline } from '@react-icons/all-files/io5/IoSunnyOutline'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
import * as config from '@/lib/config'
|
import * as config from '@/lib/config'
|
||||||
import { useDarkMode } from '@/lib/use-dark-mode'
|
import { useDarkMode } from '@/lib/use-dark-mode'
|
||||||
@@ -17,7 +16,7 @@ import styles from './styles.module.css'
|
|||||||
|
|
||||||
// TODO: merge the data and icons from PageSocial with the social links in Footer
|
// TODO: merge the data and icons from PageSocial with the social links in Footer
|
||||||
|
|
||||||
export const FooterImpl: React.FC = () => {
|
export function FooterImpl() {
|
||||||
const [hasMounted, setHasMounted] = React.useState(false)
|
const [hasMounted, setHasMounted] = React.useState(false)
|
||||||
const { isDarkMode, toggleDarkMode } = useDarkMode()
|
const { isDarkMode, toggleDarkMode } = useDarkMode()
|
||||||
const currentYear = new Date().getFullYear()
|
const currentYear = new Date().getFullYear()
|
||||||
@@ -36,7 +35,9 @@ export const FooterImpl: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<div className={styles.copyright}>Copyright {currentYear} {config.author}</div>
|
<div className={styles.copyright}>
|
||||||
|
Copyright {currentYear} {config.author}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles.settings}>
|
<div className={styles.settings}>
|
||||||
{hasMounted && (
|
{hasMounted && (
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
export const GitHubShareButton: React.FC = () => {
|
export function GitHubShareButton() {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href='https://github.com/transitive-bullshit/nextjs-notion-starter-kit'
|
href='https://github.com/transitive-bullshit/nextjs-notion-starter-kit'
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import { LoadingIcon } from './LoadingIcon'
|
import { LoadingIcon } from './LoadingIcon'
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
export const Loading: React.FC = () => (
|
export function Loading() {
|
||||||
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<LoadingIcon />
|
<LoadingIcon />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import cs from 'classnames'
|
import cs from 'classnames'
|
||||||
|
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
export const LoadingIcon = (props) => {
|
export function LoadingIcon(props: any) {
|
||||||
const { className, ...rest } = props
|
const { className, ...rest } = props
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import * as React from 'react'
|
import cs from 'classnames'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import Image from 'next/image'
|
import Image from 'next/legacy/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import { type PageBlock } from 'notion-types'
|
||||||
import cs from 'classnames'
|
|
||||||
import { PageBlock } from 'notion-types'
|
|
||||||
import { formatDate, getBlockTitle, getPageProperty } from 'notion-utils'
|
import { formatDate, getBlockTitle, getPageProperty } from 'notion-utils'
|
||||||
|
import * as React from 'react'
|
||||||
import BodyClassName from 'react-body-classname'
|
import BodyClassName from 'react-body-classname'
|
||||||
import { NotionRenderer } from 'react-notion-x'
|
import { NotionRenderer } from 'react-notion-x'
|
||||||
import TweetEmbed from 'react-tweet-embed'
|
import TweetEmbed from 'react-tweet-embed'
|
||||||
import { useSearchParam } from 'react-use'
|
import { useSearchParam } from 'react-use'
|
||||||
|
|
||||||
|
import type * as types from '@/lib/types'
|
||||||
import * as config from '@/lib/config'
|
import * as config from '@/lib/config'
|
||||||
import * as types from '@/lib/types'
|
|
||||||
import { mapImageUrl } from '@/lib/map-image-url'
|
import { mapImageUrl } from '@/lib/map-image-url'
|
||||||
import { getCanonicalPageUrl, mapPageUrl } from '@/lib/map-page-url'
|
import { getCanonicalPageUrl, mapPageUrl } from '@/lib/map-page-url'
|
||||||
import { searchNotion } from '@/lib/search-notion'
|
import { searchNotion } from '@/lib/search-notion'
|
||||||
@@ -97,7 +96,7 @@ const Modal = dynamic(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const Tweet = ({ id }: { id: string }) => {
|
function Tweet({ id }: { id: string }) {
|
||||||
return <TweetEmbed tweetId={id} />
|
return <TweetEmbed tweetId={id} />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +141,12 @@ const propertyTextValue = (
|
|||||||
return defaultFn()
|
return defaultFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotionPage: React.FC<types.PageProps> = ({
|
export function NotionPage({
|
||||||
site,
|
site,
|
||||||
recordMap,
|
recordMap,
|
||||||
error,
|
error,
|
||||||
pageId
|
pageId
|
||||||
}) => {
|
}: types.PageProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const lite = useSearchParam('lite')
|
const lite = useSearchParam('lite')
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import * as React from 'react'
|
import type * as types from 'notion-types'
|
||||||
|
|
||||||
import * as types from 'notion-types'
|
|
||||||
import { IoMoonSharp } from '@react-icons/all-files/io5/IoMoonSharp'
|
import { IoMoonSharp } from '@react-icons/all-files/io5/IoMoonSharp'
|
||||||
import { IoSunnyOutline } from '@react-icons/all-files/io5/IoSunnyOutline'
|
import { IoSunnyOutline } from '@react-icons/all-files/io5/IoSunnyOutline'
|
||||||
import cs from 'classnames'
|
import cs from 'classnames'
|
||||||
|
import * as React from 'react'
|
||||||
import { Breadcrumbs, Header, Search, useNotionContext } from 'react-notion-x'
|
import { Breadcrumbs, Header, Search, useNotionContext } from 'react-notion-x'
|
||||||
|
|
||||||
import { isSearchEnabled, navigationLinks, navigationStyle } from '@/lib/config'
|
import { isSearchEnabled, navigationLinks, navigationStyle } from '@/lib/config'
|
||||||
@@ -11,7 +10,7 @@ import { useDarkMode } from '@/lib/use-dark-mode'
|
|||||||
|
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
const ToggleThemeButton = () => {
|
function ToggleThemeButton() {
|
||||||
const [hasMounted, setHasMounted] = React.useState(false)
|
const [hasMounted, setHasMounted] = React.useState(false)
|
||||||
const { isDarkMode, toggleDarkMode } = useDarkMode()
|
const { isDarkMode, toggleDarkMode } = useDarkMode()
|
||||||
|
|
||||||
@@ -33,9 +32,11 @@ const ToggleThemeButton = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotionPageHeader: React.FC<{
|
export function NotionPageHeader({
|
||||||
|
block
|
||||||
|
}: {
|
||||||
block: types.CollectionViewPageBlock | types.PageBlock
|
block: types.CollectionViewPageBlock | types.PageBlock
|
||||||
}> = ({ block }) => {
|
}) {
|
||||||
const { components, mapPageUrl } = useNotionContext()
|
const { components, mapPageUrl } = useNotionContext()
|
||||||
|
|
||||||
if (navigationStyle === 'default') {
|
if (navigationStyle === 'default') {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import * as React from 'react'
|
import type * as types from '@/lib/types'
|
||||||
|
|
||||||
import * as types from '@/lib/types'
|
|
||||||
|
|
||||||
import { PageHead } from './PageHead'
|
import { PageHead } from './PageHead'
|
||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
|
|
||||||
export const Page404: React.FC<types.PageProps> = ({ site, pageId, error }) => {
|
export function Page404({ site, pageId, error }: types.PageProps) {
|
||||||
const title = site?.name || 'Notion Page Not Found'
|
const title = site?.name || 'Notion Page Not Found'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import { AiOutlineRetweet } from '@react-icons/all-files/ai/AiOutlineRetweet'
|
import { AiOutlineRetweet } from '@react-icons/all-files/ai/AiOutlineRetweet'
|
||||||
import { IoHeartOutline } from '@react-icons/all-files/io5/IoHeartOutline'
|
import { IoHeartOutline } from '@react-icons/all-files/io5/IoHeartOutline'
|
||||||
|
|
||||||
@@ -8,7 +6,7 @@ import styles from './styles.module.css'
|
|||||||
/**
|
/**
|
||||||
* @see https://developer.twitter.com/en/docs/twitter-for-websites/web-intents/overview
|
* @see https://developer.twitter.com/en/docs/twitter-for-websites/web-intents/overview
|
||||||
*/
|
*/
|
||||||
export const PageActions: React.FC<{ tweet: string }> = ({ tweet }) => {
|
export function PageActions({ tweet }: { tweet: string }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.pageActions}>
|
<div className={styles.pageActions}>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import * as React from 'react'
|
import { type Block, type ExtendedRecordMap } from 'notion-types'
|
||||||
|
|
||||||
import { Block, ExtendedRecordMap } from 'notion-types'
|
|
||||||
|
|
||||||
import { getPageTweet } from '@/lib/get-page-tweet'
|
import { getPageTweet } from '@/lib/get-page-tweet'
|
||||||
|
|
||||||
import { PageActions } from './PageActions'
|
import { PageActions } from './PageActions'
|
||||||
import { PageSocial } from './PageSocial'
|
import { PageSocial } from './PageSocial'
|
||||||
|
|
||||||
export const PageAside: React.FC<{
|
export function PageAside({
|
||||||
|
block,
|
||||||
|
recordMap,
|
||||||
|
isBlogPost
|
||||||
|
}: {
|
||||||
block: Block
|
block: Block
|
||||||
recordMap: ExtendedRecordMap
|
recordMap: ExtendedRecordMap
|
||||||
isBlogPost: boolean
|
isBlogPost: boolean
|
||||||
}> = ({ block, recordMap, isBlogPost }) => {
|
}) {
|
||||||
if (!block) {
|
if (!block) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import * as React from 'react'
|
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
import type * as types from '@/lib/types'
|
||||||
import * as config from '@/lib/config'
|
import * as config from '@/lib/config'
|
||||||
import * as types from '@/lib/types'
|
|
||||||
import { getSocialImageUrl } from '@/lib/get-social-image-url'
|
import { getSocialImageUrl } from '@/lib/get-social-image-url'
|
||||||
|
|
||||||
export const PageHead: React.FC<
|
export function PageHead({
|
||||||
types.PageProps & {
|
site,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
pageId,
|
||||||
|
image,
|
||||||
|
url
|
||||||
|
}: types.PageProps & {
|
||||||
title?: string
|
title?: string
|
||||||
description?: string
|
description?: string
|
||||||
image?: string
|
image?: string
|
||||||
url?: string
|
url?: string
|
||||||
}
|
}) {
|
||||||
> = ({ site, title, description, pageId, image, url }) => {
|
|
||||||
const rssFeedUrl = `${config.host}/feed`
|
const rssFeedUrl = `${config.host}/feed`
|
||||||
|
|
||||||
title = title ?? site?.name
|
title = title ?? site?.name
|
||||||
@@ -30,13 +34,20 @@ export const PageHead: React.FC<
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<meta name='apple-mobile-web-app-capable' content='yes' />
|
<meta name='apple-mobile-web-app-capable' content='yes' />
|
||||||
<meta
|
<meta name='apple-mobile-web-app-status-bar-style' content='black' />
|
||||||
name='apple-mobile-web-app-status-bar-style'
|
|
||||||
content='black'
|
|
||||||
/>
|
|
||||||
|
|
||||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fefffe" key="theme-color-light"/>
|
<meta
|
||||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#2d3439" key="theme-color-dark"/>
|
name='theme-color'
|
||||||
|
media='(prefers-color-scheme: light)'
|
||||||
|
content='#fefffe'
|
||||||
|
key='theme-color-light'
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name='theme-color'
|
||||||
|
media='(prefers-color-scheme: dark)'
|
||||||
|
content='#2d3439'
|
||||||
|
key='theme-color-dark'
|
||||||
|
/>
|
||||||
|
|
||||||
<meta name='robots' content='index,follow' />
|
<meta name='robots' content='index,follow' />
|
||||||
<meta property='og:type' content='website' />
|
<meta property='og:type' content='website' />
|
||||||
|
|||||||
@@ -101,10 +101,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.youtube .actionBgPane {
|
.youtube .actionBgPane {
|
||||||
background: #FF0000;
|
background: #ff0000;
|
||||||
}
|
}
|
||||||
.youtube:hover {
|
.youtube:hover {
|
||||||
border-color: #FF0000;
|
border-color: #ff0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium .actionBgPane {
|
.medium .actionBgPane {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as React from 'react'
|
import type * as React from 'react'
|
||||||
|
|
||||||
import cs from 'classnames'
|
import cs from 'classnames'
|
||||||
|
|
||||||
import * as config from '@/lib/config'
|
import * as config from '@/lib/config'
|
||||||
@@ -70,7 +69,7 @@ const socialLinks: SocialLink[] = [
|
|||||||
}
|
}
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
|
|
||||||
export const PageSocial: React.FC = () => {
|
export function PageSocial() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.pageSocial}>
|
<div className={styles.pageSocial}>
|
||||||
{socialLinks.map((action) => (
|
{socialLinks.map((action) => (
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ Suggestions and pull requests are highly encouraged. Have a look at the [open is
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
To develop the project locally, you'll need a recent version of Node.js and `yarn` v1 installed globally.
|
To develop the project locally, you'll need a recent version of Node.js and `pnpm` installed globally.
|
||||||
|
|
||||||
To get started, clone the repo and run `yarn` from the root directory:
|
To get started, clone the repo and run `pnpm` from the root directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/transitive-bullshit/nextjs-notion-starter-kit
|
git clone https://github.com/transitive-bullshit/nextjs-notion-starter-kit
|
||||||
cd nextjs-notion-starter-kit
|
cd nextjs-notion-starter-kit
|
||||||
yarn
|
pnpm
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that your dependencies are installed, you can run the local Next.js dev server:
|
Now that your dependencies are installed, you can run the local Next.js dev server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
You should now be able to open `http://localhost:3000` to view the webapp.
|
You should now be able to open `http://localhost:3000` to view the webapp.
|
||||||
@@ -27,7 +27,7 @@ You should now be able to open `http://localhost:3000` to view the webapp.
|
|||||||
To build for production, you can run:
|
To build for production, you can run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
Which just runs `next build` under the hood.
|
Which just runs `next build` under the hood.
|
||||||
@@ -36,48 +36,18 @@ Which just runs `next build` under the hood.
|
|||||||
|
|
||||||
If you are making changes to `react-notion-x` and want to test them out with `nextjs-notion-starter-kit`, you'll first need to [set up and build `react-notion-x` locally](https://github.com/NotionX/react-notion-x/blob/master/contributing.md).
|
If you are making changes to `react-notion-x` and want to test them out with `nextjs-notion-starter-kit`, you'll first need to [set up and build `react-notion-x` locally](https://github.com/NotionX/react-notion-x/blob/master/contributing.md).
|
||||||
|
|
||||||
Once you have `react-notion-x` set up locally, run `yarn link` from each `react-notion-x` package:
|
Once you have `react-notion-x` set up and built locally, you can link these local deps into `nextjs-notion-starter-kit`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# from react-notion-x clone
|
pnpm deps:link
|
||||||
cd packages/react-notion-x
|
|
||||||
yarn link
|
|
||||||
cd ../packages/notion-utils
|
|
||||||
yarn link
|
|
||||||
cd ../packages/notion-types
|
|
||||||
yarn link
|
|
||||||
cd ../packages/notion-client
|
|
||||||
yarn link
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can link these local deps into `nextjs-notion-starter-kit`:
|
With this setup, in one tab, you can run `pnpm dev` to keep `react-notion-x` up-to-date, and in another tab, you can run `pnpm dev` to keep `nextjs-notion-starter-kit` up-to-date.
|
||||||
|
|
||||||
```bash
|
|
||||||
# from nextjs-notion-starter-kit
|
|
||||||
yarn deps:link
|
|
||||||
```
|
|
||||||
|
|
||||||
The last step is to make sure that the Next.js project and these local dependencies are all pointing to the same versions of `react` and `react-dom`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# from react-notion-x clone
|
|
||||||
cd node_modules/react
|
|
||||||
yarn link
|
|
||||||
cd ../react-dom
|
|
||||||
yarn link
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# from nextjs-notion-starter-kit
|
|
||||||
yarn link react react-dom
|
|
||||||
```
|
|
||||||
|
|
||||||
With this setup, in one tab, you can run `yarn dev` to keep `react-notion-x` up-to-date, and in another tab, you can run `yarn dev` to keep `nextjs-notion-starter-kit` up-to-date.
|
|
||||||
|
|
||||||
### Gotchas
|
### Gotchas
|
||||||
|
|
||||||
Whenever you make a change to one of the `react-notion-x` packages, it will automatically be recompiled into its respective `build` folder, and the `yarn dev` from `nextjs-notion-starter-kit` should hot-reload it in the browser.
|
Whenever you make a change to one of the `react-notion-x` packages, it will automatically be recompiled into its respective `build` folder, and the `pnpm dev` from `nextjs-notion-starter-kit` should hot-reload it in the browser.
|
||||||
|
|
||||||
Sometimes, this process gets a little out of whack, and if you're not sure what's going on, I usually just quit one or both of the `yarn dev` commands and restart them.
|
Sometimes, this process gets a little out of whack, and if you're not sure what's going on, I usually just quit one or both of the `pnpm dev` commands and restart them.
|
||||||
|
|
||||||
If you're seeing something unexpected while debugging with Next.js, try running `rm -rf .next` to refresh the Next.js cache before running `yarn dev` again.
|
If you're seeing something unexpected while debugging with Next.js, try running `rm -rf .next` to refresh the Next.js cache before running `pnpm dev` again.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PageProps } from './types'
|
import { type PageProps } from './types'
|
||||||
|
|
||||||
export async function pageAcl({
|
export async function pageAcl({
|
||||||
site,
|
site,
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
* for optional depenencies.
|
* for optional depenencies.
|
||||||
*/
|
*/
|
||||||
import { parsePageId } from 'notion-utils'
|
import { parsePageId } from 'notion-utils'
|
||||||
import { PostHogConfig } from 'posthog-js'
|
import { type PostHogConfig } from 'posthog-js'
|
||||||
|
|
||||||
import { getEnv, getSiteConfig } from './get-config-value'
|
import { getEnv, getSiteConfig } from './get-config-value'
|
||||||
import { NavigationLink } from './site-config'
|
import { type NavigationLink } from './site-config'
|
||||||
import {
|
import {
|
||||||
NavigationStyle,
|
type NavigationStyle,
|
||||||
PageUrlOverridesInverseMap,
|
type PageUrlOverridesInverseMap,
|
||||||
PageUrlOverridesMap,
|
type PageUrlOverridesMap,
|
||||||
Site
|
type Site
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export const rootNotionPageId: string = parsePageId(
|
export const rootNotionPageId: string = parsePageId(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExtendedRecordMap } from 'notion-types'
|
import { type ExtendedRecordMap } from 'notion-types'
|
||||||
import {
|
import {
|
||||||
getCanonicalPageId as getCanonicalPageIdImpl,
|
getCanonicalPageId as getCanonicalPageIdImpl,
|
||||||
parsePageId
|
parsePageId
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import rawSiteConfig from '../site.config'
|
import rawSiteConfig from '../site.config'
|
||||||
import { SiteConfig } from './site-config'
|
import { type SiteConfig } from './site-config'
|
||||||
|
|
||||||
if (!rawSiteConfig) {
|
if (!rawSiteConfig) {
|
||||||
throw new Error(`Config error: invalid site.config.ts`)
|
throw new Error(`Config error: invalid site.config.ts`)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getPageProperty } from 'notion-utils'
|
import { getPageProperty } from 'notion-utils'
|
||||||
|
|
||||||
import * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
export function getPageTweet(
|
export function getPageTweet(
|
||||||
block: types.Block,
|
block: types.Block,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getAllPagesInSpace, uuidToId, getPageProperty } from 'notion-utils'
|
import { getAllPagesInSpace, getPageProperty, uuidToId } from 'notion-utils'
|
||||||
import pMemoize from 'p-memoize'
|
import pMemoize from 'p-memoize'
|
||||||
|
|
||||||
|
import type * as types from './types'
|
||||||
import * as config from './config'
|
import * as config from './config'
|
||||||
import * as types from './types'
|
|
||||||
import { includeNotionIdInUrls } from './config'
|
import { includeNotionIdInUrls } from './config'
|
||||||
import { getCanonicalPageId } from './get-canonical-page-id'
|
import { getCanonicalPageId } from './get-canonical-page-id'
|
||||||
import { notion } from './notion-api'
|
import { notion } from './notion-api'
|
||||||
@@ -25,15 +25,15 @@ const getAllPages = pMemoize(getAllPagesImpl, {
|
|||||||
cacheKey: (...args) => JSON.stringify(args)
|
cacheKey: (...args) => JSON.stringify(args)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getAllPagesImpl(
|
|
||||||
rootNotionPageId: string,
|
|
||||||
rootNotionSpaceId: string
|
|
||||||
): Promise<Partial<types.SiteMap>> {
|
|
||||||
const getPage = async (pageId: string, ...args) => {
|
const getPage = async (pageId: string, ...args) => {
|
||||||
console.log('\nnotion getPage', uuidToId(pageId))
|
console.log('\nnotion getPage', uuidToId(pageId))
|
||||||
return notion.getPage(pageId, ...args)
|
return notion.getPage(pageId, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getAllPagesImpl(
|
||||||
|
rootNotionPageId: string,
|
||||||
|
rootNotionSpaceId: string
|
||||||
|
): Promise<Partial<types.SiteMap>> {
|
||||||
const pageMap = await getAllPagesInSpace(
|
const pageMap = await getAllPagesInSpace(
|
||||||
rootNotionPageId,
|
rootNotionPageId,
|
||||||
rootNotionSpaceId,
|
rootNotionSpaceId,
|
||||||
@@ -48,7 +48,9 @@ async function getAllPagesImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const block = recordMap.block[pageId]?.value
|
const block = recordMap.block[pageId]?.value
|
||||||
if (!(getPageProperty<boolean|null>('Public', block, recordMap) ?? true)) {
|
if (
|
||||||
|
!(getPageProperty<boolean | null>('Public', block, recordMap) ?? true)
|
||||||
|
) {
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Block } from 'notion-types'
|
import { type Block } from 'notion-types'
|
||||||
import { defaultMapImageUrl } from 'react-notion-x'
|
import { defaultMapImageUrl } from 'react-notion-x'
|
||||||
|
|
||||||
import { defaultPageCover, defaultPageIcon } from './config'
|
import { defaultPageCover, defaultPageIcon } from './config'
|
||||||
|
|
||||||
export const mapImageUrl = (url: string, block: Block) => {
|
export const mapImageUrl = (url: string | undefined, block: Block) => {
|
||||||
if (url === defaultPageCover || url === defaultPageIcon) {
|
if (url === defaultPageCover || url === defaultPageIcon) {
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ExtendedRecordMap } from 'notion-types'
|
import { type ExtendedRecordMap } from 'notion-types'
|
||||||
import { parsePageId, uuidToId } from 'notion-utils'
|
import { parsePageId, uuidToId } from 'notion-utils'
|
||||||
|
|
||||||
import { includeNotionIdInUrls } from './config'
|
import { includeNotionIdInUrls } from './config'
|
||||||
import { getCanonicalPageId } from './get-canonical-page-id'
|
import { getCanonicalPageId } from './get-canonical-page-id'
|
||||||
import { Site } from './types'
|
import { type Site } from './types'
|
||||||
|
|
||||||
// include UUIDs in page URLs during local development but not in production
|
// include UUIDs in page URLs during local development but not in production
|
||||||
// (they're nice for debugging and speed up local dev)
|
// (they're nice for debugging and speed up local dev)
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { ExtendedRecordMap, SearchParams, SearchResults } from 'notion-types'
|
import {
|
||||||
|
type ExtendedRecordMap,
|
||||||
|
type SearchParams,
|
||||||
|
type SearchResults
|
||||||
|
} from 'notion-types'
|
||||||
import { mergeRecordMaps } from 'notion-utils'
|
import { mergeRecordMaps } from 'notion-utils'
|
||||||
import pMap from 'p-map'
|
import pMap from 'p-map'
|
||||||
import pMemoize from 'p-memoize'
|
import pMemoize from 'p-memoize'
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import got from 'got'
|
import ky from 'ky'
|
||||||
import lqip from 'lqip-modern'
|
import lqip from 'lqip-modern'
|
||||||
import { ExtendedRecordMap, PreviewImage, PreviewImageMap } from 'notion-types'
|
import {
|
||||||
|
type ExtendedRecordMap,
|
||||||
|
type PreviewImage,
|
||||||
|
type PreviewImageMap
|
||||||
|
} from 'notion-types'
|
||||||
import { getPageImageUrls, normalizeUrl } from 'notion-utils'
|
import { getPageImageUrls, normalizeUrl } from 'notion-utils'
|
||||||
import pMap from 'p-map'
|
import pMap from 'p-map'
|
||||||
import pMemoize from 'p-memoize'
|
import pMemoize from 'p-memoize'
|
||||||
@@ -49,7 +53,7 @@ async function createPreviewImage(
|
|||||||
console.warn(`redis error get "${cacheKey}"`, err.message)
|
console.warn(`redis error get "${cacheKey}"`, err.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { body } = await got(url, { responseType: 'buffer' })
|
const body = await ky(url).arrayBuffer()
|
||||||
const result = await lqip(body)
|
const result = await lqip(body)
|
||||||
console.log('lqip', { ...result.metadata, url, cacheKey })
|
console.log('lqip', { ...result.metadata, url, cacheKey })
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExtendedRecordMap } from 'notion-types'
|
import { type ExtendedRecordMap } from 'notion-types'
|
||||||
import { parsePageId } from 'notion-utils'
|
import { parsePageId } from 'notion-utils'
|
||||||
|
|
||||||
import * as acl from './acl'
|
import * as acl from './acl'
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
// import ky from 'ky'
|
|
||||||
import ExpiryMap from 'expiry-map'
|
import ExpiryMap from 'expiry-map'
|
||||||
import fetch from 'isomorphic-unfetch'
|
|
||||||
import pMemoize from 'p-memoize'
|
import pMemoize from 'p-memoize'
|
||||||
|
|
||||||
import * as types from './types'
|
import type * as types from './types'
|
||||||
import { api } from './config'
|
import { api } from './config'
|
||||||
|
|
||||||
export const searchNotion = pMemoize(searchNotionImpl, {
|
export const searchNotion = pMemoize(searchNotionImpl, {
|
||||||
cacheKey: (args) => args[0]?.query,
|
cacheKey: (args) => args[0]?.query,
|
||||||
cache: new ExpiryMap(10000)
|
cache: new ExpiryMap(10_000)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function searchNotionImpl(
|
async function searchNotionImpl(
|
||||||
@@ -29,7 +27,7 @@ async function searchNotionImpl(
|
|||||||
// convert non-2xx HTTP responses into errors
|
// convert non-2xx HTTP responses into errors
|
||||||
const error: any = new Error(res.statusText)
|
const error: any = new Error(res.statusText)
|
||||||
error.response = res
|
error.response = res
|
||||||
return Promise.reject(error)
|
throw error
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
export interface SiteConfig {
|
export interface SiteConfig {
|
||||||
rootNotionPageId: string
|
rootNotionPageId: string
|
||||||
@@ -16,7 +16,7 @@ export interface SiteConfig {
|
|||||||
newsletter?: string
|
newsletter?: string
|
||||||
youtube?: string
|
youtube?: string
|
||||||
zhihu?: string
|
zhihu?: string
|
||||||
mastodon?: string;
|
mastodon?: string
|
||||||
|
|
||||||
defaultPageIcon?: string | null
|
defaultPageIcon?: string | null
|
||||||
defaultPageCover?: string | null
|
defaultPageCover?: string | null
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ExtendedRecordMap, PageMap } from 'notion-types'
|
import { type ParsedUrlQuery } from 'node:querystring'
|
||||||
import { ParsedUrlQuery } from 'querystring'
|
|
||||||
|
import { type ExtendedRecordMap, type PageMap } from 'notion-types'
|
||||||
|
|
||||||
export * from 'notion-types'
|
export * from 'notion-types'
|
||||||
|
|
||||||
|
|||||||
2
license
2
license
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Travis Fischer
|
Copyright (c) 2024 Travis Fischer
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
|||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||||
|
|||||||
@@ -1,22 +1,65 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
import bundleAnalyzer from '@next/bundle-analyzer'
|
||||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const withBundleAnalyzer = bundleAnalyzer({
|
||||||
enabled: process.env.ANALYZE === 'true'
|
enabled: process.env.ANALYZE === 'true'
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = withBundleAnalyzer({
|
export default withBundleAnalyzer({
|
||||||
staticPageGenerationTimeout: 300,
|
staticPageGenerationTimeout: 300,
|
||||||
images: {
|
images: {
|
||||||
domains: [
|
remotePatterns: [
|
||||||
'www.notion.so',
|
{
|
||||||
'notion.so',
|
protocol: 'https',
|
||||||
'images.unsplash.com',
|
hostname: 'www.notion.so',
|
||||||
'pbs.twimg.com',
|
pathname: '**'
|
||||||
'abs.twimg.com',
|
},
|
||||||
's3.us-west-2.amazonaws.com',
|
{
|
||||||
'transitivebullsh.it'
|
protocol: 'https',
|
||||||
|
hostname: 'notion.so',
|
||||||
|
pathname: '**'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'images.unsplash.com',
|
||||||
|
pathname: '**'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'pbs.twimg.com',
|
||||||
|
pathname: '**'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'abs.twimg.com',
|
||||||
|
pathname: '**'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 's3.us-west-2.amazonaws.com',
|
||||||
|
pathname: '**'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'transitivebullsh.it',
|
||||||
|
pathname: '**'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
formats: ['image/avif', 'image/webp'],
|
formats: ['image/avif', 'image/webp'],
|
||||||
dangerouslyAllowSVG: true,
|
dangerouslyAllowSVG: true,
|
||||||
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
|
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
|
||||||
|
},
|
||||||
|
webpack: (config, _context) => {
|
||||||
|
// Workaround for ensuring that `react` and `react-dom` resolve correctly
|
||||||
|
// when using a locally-linked version of `react-notion-x`.
|
||||||
|
// @see https://github.com/vercel/next.js/issues/50391
|
||||||
|
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
config.resolve.alias.react = path.resolve(dirname, 'node_modules/react')
|
||||||
|
config.resolve.alias['react-dom'] = path.resolve(
|
||||||
|
dirname,
|
||||||
|
'node_modules/react-dom'
|
||||||
|
)
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
65
package.json
65
package.json
@@ -7,21 +7,25 @@
|
|||||||
"repository": "transitive-bullshit/nextjs-notion-starter-kit",
|
"repository": "transitive-bullshit/nextjs-notion-starter-kit",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"deploy": "vercel deploy",
|
"deploy": "vercel deploy",
|
||||||
"deps": "run-s deps:*",
|
"deps:update": "[ -z $GITHUB_ACTIONS ] && pnpm up -L notion-client notion-types notion-utils react-notion-x || echo 'Skipping deps:update on CI'",
|
||||||
"deps:update": "[ -z $GITHUB_ACTIONS ] && yarn add notion-client notion-types notion-utils react-notion-x || echo 'Skipping deps:update on CI'",
|
"deps:link": "[ -z $GITHUB_ACTIONS ] && run-s deps:link:* || echo 'Skipping deps:update on CI'",
|
||||||
"deps:link": "[ -z $GITHUB_ACTIONS ] && yarn link notion-client notion-types notion-utils react-notion-x || echo 'Skipping deps:link on CI'",
|
"deps:link:notion-types": "pnpm link ../react-notion-x/packages/notion-types",
|
||||||
|
"deps:link:notion-utils": "pnpm link ../react-notion-x/packages/notion-utils",
|
||||||
|
"deps:link:notion-client": "pnpm link ../react-notion-x/packages/notion-client",
|
||||||
|
"deps:link:react-notion-x": "pnpm link ../react-notion-x/packages/react-notion-x",
|
||||||
"analyze": "cross-env ANALYZE=true next build",
|
"analyze": "cross-env ANALYZE=true next build",
|
||||||
"analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
|
"analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
|
||||||
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build",
|
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build",
|
||||||
"test": "run-p test:*",
|
"test": "run-p test:*",
|
||||||
"test:lint": "eslint '**/*.{ts,tsx}'",
|
"test:lint": "eslint .",
|
||||||
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
|
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -29,21 +33,21 @@
|
|||||||
"@keyvhq/core": "^1.6.9",
|
"@keyvhq/core": "^1.6.9",
|
||||||
"@keyvhq/redis": "^1.6.10",
|
"@keyvhq/redis": "^1.6.10",
|
||||||
"@react-icons/all-files": "^4.1.0",
|
"@react-icons/all-files": "^4.1.0",
|
||||||
"@vercel/og": "^0.0.19",
|
"@vercel/og": "^0.6.3",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.5.1",
|
||||||
"date-fns": "^2.28.0",
|
"date-fns": "^2.30.0",
|
||||||
"expiry-map": "^2.0.0",
|
"expiry-map": "^2.0.0",
|
||||||
"fathom-client": "^3.4.1",
|
"fathom-client": "^3.4.1",
|
||||||
"got": "^12.0.3",
|
"ky": "^1.7.2",
|
||||||
"isomorphic-unfetch": "^3.1.0",
|
"lqip-modern": "^2.1.0",
|
||||||
"lqip-modern": "^2.0.0",
|
"next": "^15.0.2",
|
||||||
"next": "12",
|
|
||||||
"notion-client": "^6.15.6",
|
"notion-client": "^6.15.6",
|
||||||
"notion-types": "^6.15.6",
|
"notion-types": "^6.15.6",
|
||||||
"notion-utils": "^6.15.6",
|
"notion-utils": "^6.15.6",
|
||||||
"p-map": "^5.3.0",
|
"p-map": "^7.0.2",
|
||||||
"p-memoize": "^6.0.1",
|
"p-memoize": "^7.1.1",
|
||||||
"posthog-js": "^1.20.2",
|
"posthog-js": "^1.20.2",
|
||||||
|
"prismjs": "^1.29.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-body-classname": "^1.3.1",
|
"react-body-classname": "^1.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -53,20 +57,29 @@
|
|||||||
"rss": "^1.2.2"
|
"rss": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.3.1",
|
"@fisch0920/eslint-config": "^1.4.0",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^3.3.1",
|
"@next/bundle-analyzer": "^15.0.2",
|
||||||
"@types/node": "^18.8.5",
|
"@types/node": "^22.8.6",
|
||||||
"@types/react": "^18.0.21",
|
"@types/react": "^18.0.21",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
|
||||||
"@typescript-eslint/parser": "^5.40.0",
|
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^8.25.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"npm-run-all2": "^7.0.1",
|
||||||
"eslint-plugin-react": "^7.31.10",
|
"prettier": "^3.3.3",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"typescript": "^5.6.3"
|
||||||
"npm-run-all": "^4.1.5",
|
},
|
||||||
"prettier": "^2.7.1",
|
"dependenciesMeta": {
|
||||||
"typescript": "^4.8.4"
|
"notion-client": {
|
||||||
|
"injected": true
|
||||||
|
},
|
||||||
|
"notion-types": {
|
||||||
|
"injected": true
|
||||||
|
},
|
||||||
|
"notion-utils": {
|
||||||
|
"injected": true
|
||||||
|
},
|
||||||
|
"react-notion-x": {
|
||||||
|
"injected": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"cacheable-request": {
|
"cacheable-request": {
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
import { Page404 } from '@/components/Page404'
|
export { Page404 as default } from '@/components/Page404'
|
||||||
|
|
||||||
export default Page404
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import * as React from 'react'
|
import { type GetStaticProps } from 'next'
|
||||||
import { GetStaticProps } from 'next'
|
|
||||||
|
|
||||||
import { NotionPage } from '@/components/NotionPage'
|
import { NotionPage } from '@/components/NotionPage'
|
||||||
import { domain, isDev } from '@/lib/config'
|
import { domain, isDev } from '@/lib/config'
|
||||||
import { getSiteMap } from '@/lib/get-site-map'
|
import { getSiteMap } from '@/lib/get-site-map'
|
||||||
import { resolveNotionPage } from '@/lib/resolve-notion-page'
|
import { resolveNotionPage } from '@/lib/resolve-notion-page'
|
||||||
import { PageProps, Params } from '@/lib/types'
|
import { type PageProps, type Params } from '@/lib/types'
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps<PageProps, Params> = async (
|
export const getStaticProps: GetStaticProps<PageProps, Params> = async (
|
||||||
context
|
context
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
// global styles shared across the entire site
|
|
||||||
import * as React from 'react'
|
|
||||||
import type { AppProps } from 'next/app'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
|
|
||||||
import * as Fathom from 'fathom-client'
|
|
||||||
// used for rendering equations (optional)
|
// used for rendering equations (optional)
|
||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
import posthog from 'posthog-js'
|
|
||||||
// used for code syntax highlighting (optional)
|
// used for code syntax highlighting (optional)
|
||||||
import 'prismjs/themes/prism-coy.css'
|
import 'prismjs/themes/prism-coy.css'
|
||||||
// core styles shared by all of react-notion-x (required)
|
// core styles shared by all of react-notion-x (required)
|
||||||
import 'react-notion-x/src/styles.css'
|
import 'react-notion-x/src/styles.css'
|
||||||
|
// global styles shared across the entire site
|
||||||
import 'styles/global.css'
|
import 'styles/global.css'
|
||||||
// this might be better for dark mode
|
// this might be better for dark mode
|
||||||
// import 'prismjs/themes/prism-okaidia.css'
|
// import 'prismjs/themes/prism-okaidia.css'
|
||||||
@@ -19,6 +13,12 @@ import 'styles/notion.css'
|
|||||||
// global style overrides for prism theme (optional)
|
// global style overrides for prism theme (optional)
|
||||||
import 'styles/prism-theme.css'
|
import 'styles/prism-theme.css'
|
||||||
|
|
||||||
|
import type { AppProps } from 'next/app'
|
||||||
|
import * as Fathom from 'fathom-client'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import posthog from 'posthog-js'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
import { bootstrap } from '@/lib/bootstrap-client'
|
import { bootstrap } from '@/lib/bootstrap-client'
|
||||||
import {
|
import {
|
||||||
fathomConfig,
|
fathomConfig,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import * as React from 'react'
|
|
||||||
import Document, { Head, Html, Main, NextScript } from 'next/document'
|
|
||||||
|
|
||||||
import { IconContext } from '@react-icons/all-files'
|
import { IconContext } from '@react-icons/all-files'
|
||||||
|
import Document, { Head, Html, Main, NextScript } from 'next/document'
|
||||||
|
|
||||||
export default class MyDocument extends Document {
|
export default class MyDocument extends Document {
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
import { ErrorPage } from '@/components/ErrorPage'
|
export { ErrorPage as default } from '@/components/ErrorPage'
|
||||||
|
|
||||||
export default ErrorPage
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import ky from 'ky'
|
||||||
|
import { type NextApiRequest, type NextApiResponse } from 'next'
|
||||||
import got from 'got'
|
import { type PageBlock } from 'notion-types'
|
||||||
import { PageBlock } from 'notion-types'
|
|
||||||
import {
|
import {
|
||||||
getBlockIcon,
|
getBlockIcon,
|
||||||
getBlockTitle,
|
getBlockTitle,
|
||||||
@@ -13,9 +12,12 @@ import {
|
|||||||
import * as libConfig from '@/lib/config'
|
import * as libConfig from '@/lib/config'
|
||||||
import { mapImageUrl } from '@/lib/map-image-url'
|
import { mapImageUrl } from '@/lib/map-image-url'
|
||||||
import { notion } from '@/lib/notion-api'
|
import { notion } from '@/lib/notion-api'
|
||||||
import { NotionPageInfo } from '@/lib/types'
|
import { type NotionPageInfo } from '@/lib/types'
|
||||||
|
|
||||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
export default async function notionPageInfo(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
if (req.method !== 'POST') {
|
if (req.method !== 'POST') {
|
||||||
return res.status(405).send({ error: 'method not allowed' })
|
return res.status(405).send({ error: 'method not allowed' })
|
||||||
}
|
}
|
||||||
@@ -125,9 +127,9 @@ async function isUrlReachable(url: string | null): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await got.head(url)
|
await ky.head(url)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { type NextApiRequest, type NextApiResponse } from 'next'
|
||||||
|
|
||||||
import * as types from '../../lib/types'
|
import type * as types from '../../lib/types'
|
||||||
import { search } from '../../lib/notion'
|
import { search } from '../../lib/notion'
|
||||||
|
|
||||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
export default async function searchNotion(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
if (req.method !== 'POST') {
|
if (req.method !== 'POST') {
|
||||||
return res.status(405).send({ error: 'method not allowed' })
|
return res.status(405).send({ error: 'method not allowed' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import * as React from 'react'
|
|
||||||
import { NextRequest } from 'next/server'
|
|
||||||
|
|
||||||
import { ImageResponse } from '@vercel/og'
|
import { ImageResponse } from '@vercel/og'
|
||||||
|
import ky from 'ky'
|
||||||
|
import { type NextRequest } from 'next/server'
|
||||||
|
|
||||||
import { api, apiHost, rootNotionPageId } from '@/lib/config'
|
import { api, apiHost, rootNotionPageId } from '@/lib/config'
|
||||||
import { NotionPageInfo } from '@/lib/types'
|
import { type NotionPageInfo } from '@/lib/types'
|
||||||
|
|
||||||
const interRegularFontP = fetch(
|
const interRegularFontP = ky(
|
||||||
new URL('../../public/fonts/Inter-Regular.ttf', import.meta.url)
|
new URL('../../public/fonts/Inter-Regular.ttf', import.meta.url)
|
||||||
).then((res) => res.arrayBuffer())
|
).arrayBuffer()
|
||||||
|
|
||||||
const interBoldFontP = fetch(
|
const interBoldFontP = ky(
|
||||||
new URL('../../public/fonts/Inter-SemiBold.ttf', import.meta.url)
|
new URL('../../public/fonts/Inter-SemiBold.ttf', import.meta.url)
|
||||||
).then((res) => res.arrayBuffer())
|
).arrayBuffer()
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
runtime: 'experimental-edge'
|
runtime: 'experimental-edge'
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GetServerSideProps } from 'next'
|
import type { GetServerSideProps } from 'next'
|
||||||
|
import { type ExtendedRecordMap } from 'notion-types'
|
||||||
import { ExtendedRecordMap } from 'notion-types'
|
|
||||||
import {
|
import {
|
||||||
getBlockParentPage,
|
getBlockParentPage,
|
||||||
getBlockTitle,
|
getBlockTitle,
|
||||||
@@ -98,4 +97,6 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
|||||||
return { props: {} }
|
return { props: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => null
|
export default function noop() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
import { NotionPage } from '@/components/NotionPage'
|
import { NotionPage } from '@/components/NotionPage'
|
||||||
import { domain } from '@/lib/config'
|
import { domain } from '@/lib/config'
|
||||||
import { resolveNotionPage } from '@/lib/resolve-notion-page'
|
import { resolveNotionPage } from '@/lib/resolve-notion-page'
|
||||||
|
|||||||
@@ -42,4 +42,6 @@ Sitemap: ${host}/sitemap.xml
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => null
|
export default function noop() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { GetServerSideProps } from 'next'
|
import type { GetServerSideProps } from 'next'
|
||||||
|
|
||||||
|
import type { SiteMap } from '@/lib/types'
|
||||||
import { host } from '@/lib/config'
|
import { host } from '@/lib/config'
|
||||||
import { getSiteMap } from '@/lib/get-site-map'
|
import { getSiteMap } from '@/lib/get-site-map'
|
||||||
import type { SiteMap } from '@/lib/types'
|
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
||||||
if (req.method !== 'GET') {
|
if (req.method !== 'GET') {
|
||||||
@@ -54,4 +54,6 @@ const createSitemap = (siteMap: SiteMap) =>
|
|||||||
</urlset>
|
</urlset>
|
||||||
`
|
`
|
||||||
|
|
||||||
export default () => null
|
export default function noop() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
4458
pnpm-lock.yaml
generated
Normal file
4458
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,15 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2016",
|
"target": "es2017",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
|
|||||||
Reference in New Issue
Block a user