mirror of
https://github.com/d0zingcat/nextjs-notion-starter-kit.git
synced 2026-05-13 15:09:47 +00:00
Merge pull request #707 from transitive-bullshit/feature/upkeep-june-2025
This commit is contained in:
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@@ -1,40 +1,29 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test Node.js ${{ matrix.node-version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
node-version:
|
||||
- 18
|
||||
- 20
|
||||
- 22
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 9.12.2
|
||||
run_install: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --strict-peer-dependencies
|
||||
|
||||
- name: Run test
|
||||
run: pnpm test
|
||||
- run: pnpm install --frozen-lockfile --strict-peer-dependencies
|
||||
- run: pnpm test
|
||||
|
||||
# TODO Enable those lines below if you use a Redis cache, you'll also need to configure GitHub Repository Secrets
|
||||
# env:
|
||||
|
||||
@@ -4,6 +4,3 @@ dist/
|
||||
node_modules/
|
||||
.next/
|
||||
.vercel/
|
||||
|
||||
.demo/
|
||||
.renderer/
|
||||
|
||||
11
.prettierrc
11
.prettierrc
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"semi": false,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export function FooterImpl() {
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
const onToggleDarkMode = React.useCallback(
|
||||
(e) => {
|
||||
(e: any) => {
|
||||
e.preventDefault()
|
||||
toggleDarkMode()
|
||||
},
|
||||
|
||||
@@ -39,36 +39,67 @@ const Code = dynamic(() =>
|
||||
import('react-notion-x/build/third-party/code').then(async (m) => {
|
||||
// add / remove any prism syntaxes here
|
||||
await Promise.allSettled([
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-markup-templating.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-markup.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-bash.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-c.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-cpp.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-csharp.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-docker.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-java.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-js-templates.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-coffeescript.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-diff.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-git.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-go.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-graphql.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-handlebars.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-less.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-makefile.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-markdown.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-objectivec.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-ocaml.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-python.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-reason.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-rust.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-sass.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-scss.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-solidity.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-sql.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-stylus.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-swift.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-wasm.js'),
|
||||
// @ts-expect-error Ignore prisma types
|
||||
import('prismjs/components/prism-yaml.js')
|
||||
])
|
||||
return m.Code
|
||||
@@ -112,7 +143,7 @@ function Tweet({ id }: { id: string }) {
|
||||
}
|
||||
|
||||
const propertyLastEditedTimeValue = (
|
||||
{ block, pageHeader },
|
||||
{ block, pageHeader }: any,
|
||||
defaultFn: () => React.ReactNode
|
||||
) => {
|
||||
if (pageHeader && block?.last_edited_time) {
|
||||
@@ -125,7 +156,7 @@ const propertyLastEditedTimeValue = (
|
||||
}
|
||||
|
||||
const propertyDateValue = (
|
||||
{ data, schema, pageHeader },
|
||||
{ data, schema, pageHeader }: any,
|
||||
defaultFn: () => React.ReactNode
|
||||
) => {
|
||||
if (pageHeader && schema?.name?.toLowerCase() === 'published') {
|
||||
@@ -142,7 +173,7 @@ const propertyDateValue = (
|
||||
}
|
||||
|
||||
const propertyTextValue = (
|
||||
{ schema, pageHeader },
|
||||
{ schema, pageHeader }: any,
|
||||
defaultFn: () => React.ReactNode
|
||||
) => {
|
||||
if (pageHeader && schema?.name?.toLowerCase() === 'author') {
|
||||
@@ -189,11 +220,11 @@ export function NotionPage({
|
||||
if (lite) params.lite = lite
|
||||
|
||||
const searchParams = new URLSearchParams(params)
|
||||
return mapPageUrl(site, recordMap, searchParams)
|
||||
return site ? mapPageUrl(site, recordMap!, searchParams) : undefined
|
||||
}, [site, recordMap, lite])
|
||||
|
||||
const keys = Object.keys(recordMap?.block || {})
|
||||
const block = recordMap?.block?.[keys[0]]?.value
|
||||
const block = recordMap?.block?.[keys[0]!]?.value
|
||||
|
||||
// const isRootPage =
|
||||
// parsePageId(block?.id) === parsePageId(site?.rootNotionPageId)
|
||||
@@ -205,7 +236,11 @@ export function NotionPage({
|
||||
|
||||
const pageAside = React.useMemo(
|
||||
() => (
|
||||
<PageAside block={block} recordMap={recordMap} isBlogPost={isBlogPost} />
|
||||
<PageAside
|
||||
block={block!}
|
||||
recordMap={recordMap!}
|
||||
isBlogPost={isBlogPost}
|
||||
/>
|
||||
),
|
||||
[block, recordMap, isBlogPost]
|
||||
)
|
||||
@@ -238,8 +273,9 @@ export function NotionPage({
|
||||
g.block = block
|
||||
}
|
||||
|
||||
const canonicalPageUrl =
|
||||
!config.isDev && getCanonicalPageUrl(site, recordMap)(pageId)
|
||||
const canonicalPageUrl = config.isDev
|
||||
? undefined
|
||||
: getCanonicalPageUrl(site, recordMap)(pageId)
|
||||
|
||||
const socialImage = mapImageUrl(
|
||||
getPageProperty<string>('Social Image', block, recordMap) ||
|
||||
@@ -286,7 +322,7 @@ export function NotionPage({
|
||||
defaultPageCoverPosition={config.defaultPageCoverPosition}
|
||||
mapPageUrl={siteMapPageUrl}
|
||||
mapImageUrl={mapImageUrl}
|
||||
searchNotion={config.isSearchEnabled ? searchNotion : null}
|
||||
searchNotion={config.isSearchEnabled ? searchNotion : undefined}
|
||||
pageAside={pageAside}
|
||||
footer={footer}
|
||||
/>
|
||||
|
||||
@@ -51,7 +51,7 @@ export function NotionPageHeader({
|
||||
<div className='notion-nav-header-rhs breadcrumbs'>
|
||||
{navigationLinks
|
||||
?.map((link, index) => {
|
||||
if (!link.pageId && !link.url) {
|
||||
if (!link?.pageId && !link?.url) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
line-height: 1.5;
|
||||
color: rgb(55, 53, 47);
|
||||
caret-color: rgb(55, 53, 47);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica,
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica,
|
||||
'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
22
eslint.config.js
Normal file
22
eslint.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { config } from '@fisch0920/config/eslint'
|
||||
|
||||
export default [
|
||||
...config,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
'react/prop-types': 'off',
|
||||
'unicorn/no-array-reduce': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'unicorn/prefer-global-this': 'off',
|
||||
'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'
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -4,7 +4,7 @@ export async function pageAcl({
|
||||
site,
|
||||
recordMap,
|
||||
pageId
|
||||
}: PageProps): Promise<PageProps> {
|
||||
}: PageProps): Promise<PageProps | undefined> {
|
||||
if (!site) {
|
||||
return {
|
||||
error: {
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
import { parsePageId } from 'notion-utils'
|
||||
import { type PostHogConfig } from 'posthog-js'
|
||||
|
||||
import { getEnv, getSiteConfig } from './get-config-value'
|
||||
import {
|
||||
getEnv,
|
||||
getRequiredSiteConfig,
|
||||
getSiteConfig
|
||||
} from './get-config-value'
|
||||
import { type NavigationLink } from './site-config'
|
||||
import {
|
||||
type NavigationStyle,
|
||||
@@ -19,17 +23,15 @@ import {
|
||||
export const rootNotionPageId: string = parsePageId(
|
||||
getSiteConfig('rootNotionPageId'),
|
||||
{ uuid: false }
|
||||
)
|
||||
)!
|
||||
|
||||
if (!rootNotionPageId) {
|
||||
throw new Error('Config error invalid "rootNotionPageId"')
|
||||
}
|
||||
|
||||
// if you want to restrict pages to a single notion workspace (optional)
|
||||
export const rootNotionSpaceId: string | null = parsePageId(
|
||||
getSiteConfig('rootNotionSpaceId', null),
|
||||
{ uuid: true }
|
||||
)
|
||||
export const rootNotionSpaceId: string | null =
|
||||
parsePageId(getSiteConfig('rootNotionSpaceId'), { uuid: true }) ?? null
|
||||
|
||||
export const pageUrlOverrides = cleanPageUrlMap(
|
||||
getSiteConfig('pageUrlOverrides', {}) || {},
|
||||
@@ -47,24 +49,24 @@ export const environment = process.env.NODE_ENV || 'development'
|
||||
export const isDev = environment === 'development'
|
||||
|
||||
// general site config
|
||||
export const name: string = getSiteConfig('name')
|
||||
export const author: string = getSiteConfig('author')
|
||||
export const domain: string = getSiteConfig('domain')
|
||||
export const name: string = getRequiredSiteConfig('name')
|
||||
export const author: string = getRequiredSiteConfig('author')
|
||||
export const domain: string = getRequiredSiteConfig('domain')
|
||||
export const description: string = getSiteConfig('description', 'Notion Blog')
|
||||
export const language: string = getSiteConfig('language', 'en')
|
||||
|
||||
// social accounts
|
||||
export const twitter: string | null = getSiteConfig('twitter', null)
|
||||
export const mastodon: string | null = getSiteConfig('mastodon', null)
|
||||
export const github: string | null = getSiteConfig('github', null)
|
||||
export const youtube: string | null = getSiteConfig('youtube', null)
|
||||
export const linkedin: string | null = getSiteConfig('linkedin', null)
|
||||
export const newsletter: string | null = getSiteConfig('newsletter', null)
|
||||
export const zhihu: string | null = getSiteConfig('zhihu', null)
|
||||
export const twitter: string | undefined = getSiteConfig('twitter')
|
||||
export const mastodon: string | undefined = getSiteConfig('mastodon')
|
||||
export const github: string | undefined = getSiteConfig('github')
|
||||
export const youtube: string | undefined = getSiteConfig('youtube')
|
||||
export const linkedin: string | undefined = getSiteConfig('linkedin')
|
||||
export const newsletter: string | undefined = getSiteConfig('newsletter')
|
||||
export const zhihu: string | undefined = getSiteConfig('zhihu')
|
||||
|
||||
export const getMastodonHandle = (): string | null => {
|
||||
export const getMastodonHandle = (): string | undefined => {
|
||||
if (!mastodon) {
|
||||
return null
|
||||
return
|
||||
}
|
||||
|
||||
// Since Mastodon is decentralized, handles include the instance domain name.
|
||||
@@ -74,14 +76,10 @@ export const getMastodonHandle = (): string | null => {
|
||||
}
|
||||
|
||||
// default notion values for site-wide consistency (optional; may be overridden on a per-page basis)
|
||||
export const defaultPageIcon: string | null = getSiteConfig(
|
||||
'defaultPageIcon',
|
||||
null
|
||||
)
|
||||
export const defaultPageCover: string | null = getSiteConfig(
|
||||
'defaultPageCover',
|
||||
null
|
||||
)
|
||||
export const defaultPageIcon: string | undefined =
|
||||
getSiteConfig('defaultPageIcon')
|
||||
export const defaultPageCover: string | undefined =
|
||||
getSiteConfig('defaultPageCover')
|
||||
export const defaultPageCoverPosition: number = getSiteConfig(
|
||||
'defaultPageCoverPosition',
|
||||
0.5
|
||||
@@ -104,7 +102,7 @@ export const navigationStyle: NavigationStyle = getSiteConfig(
|
||||
'default'
|
||||
)
|
||||
|
||||
export const navigationLinks: Array<NavigationLink | null> = getSiteConfig(
|
||||
export const navigationLinks: Array<NavigationLink | undefined> = getSiteConfig(
|
||||
'navigationLinks',
|
||||
null
|
||||
)
|
||||
@@ -115,19 +113,18 @@ export const isSearchEnabled: boolean = getSiteConfig('isSearchEnabled', true)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Optional redis instance for persisting preview images
|
||||
export const isRedisEnabled: boolean =
|
||||
getSiteConfig('isRedisEnabled', false) || !!getEnv('REDIS_ENABLED', null)
|
||||
export const isRedisEnabled: boolean = getSiteConfig('isRedisEnabled', false)
|
||||
|
||||
// (if you want to enable redis, only REDIS_HOST and REDIS_PASSWORD are required)
|
||||
// we recommend that you store these in a local `.env` file
|
||||
export const redisHost: string | null = getEnv('REDIS_HOST', null)
|
||||
export const redisPassword: string | null = getEnv('REDIS_PASSWORD', null)
|
||||
export const redisUser: string = getEnv('REDIS_USER', 'default')
|
||||
export const redisHost: string | undefined = getEnv('REDIS_HOST')
|
||||
export const redisPassword: string | undefined = getEnv('REDIS_PASSWORD')
|
||||
export const redisUser: string | undefined = getEnv('REDIS_USER', 'default')
|
||||
export const redisUrl = getEnv(
|
||||
'REDIS_URL',
|
||||
`redis://${redisUser}:${redisPassword}@${redisHost}`
|
||||
)
|
||||
export const redisNamespace: string | null = getEnv(
|
||||
export const redisNamespace: string | undefined = getEnv(
|
||||
'REDIS_NAMESPACE',
|
||||
'preview-images'
|
||||
)
|
||||
@@ -160,7 +157,7 @@ export const site: Site = {
|
||||
description
|
||||
}
|
||||
|
||||
export const fathomId = isDev ? null : process.env.NEXT_PUBLIC_FATHOM_ID
|
||||
export const fathomId = isDev ? undefined : process.env.NEXT_PUBLIC_FATHOM_ID
|
||||
export const fathomConfig = fathomId
|
||||
? {
|
||||
excludedDomains: ['localhost', 'localhost:3000']
|
||||
@@ -211,7 +208,7 @@ function invertPageUrlOverrides(
|
||||
pageUrlOverrides: PageUrlOverridesMap
|
||||
): PageUrlOverridesInverseMap {
|
||||
return Object.keys(pageUrlOverrides).reduce((acc, uri) => {
|
||||
const pageId = pageUrlOverrides[uri]
|
||||
const pageId = pageUrlOverrides[uri]!
|
||||
|
||||
return {
|
||||
...acc,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isRedisEnabled, redisNamespace, redisUrl } from './config'
|
||||
|
||||
let db: Keyv
|
||||
if (isRedisEnabled) {
|
||||
const keyvRedis = new KeyvRedis(redisUrl)
|
||||
const keyvRedis = new KeyvRedis(redisUrl!)
|
||||
db = new Keyv({ store: keyvRedis, namespace: redisNamespace || undefined })
|
||||
} else {
|
||||
db = new Keyv()
|
||||
|
||||
@@ -10,18 +10,20 @@ export function getCanonicalPageId(
|
||||
pageId: string,
|
||||
recordMap: ExtendedRecordMap,
|
||||
{ uuid = true }: { uuid?: boolean } = {}
|
||||
): string | null {
|
||||
): string | undefined {
|
||||
const cleanPageId = parsePageId(pageId, { uuid: false })
|
||||
if (!cleanPageId) {
|
||||
return null
|
||||
return
|
||||
}
|
||||
|
||||
const override = inversePageUrlOverrides[cleanPageId]
|
||||
if (override) {
|
||||
return override
|
||||
} else {
|
||||
return getCanonicalPageIdImpl(pageId, recordMap, {
|
||||
uuid
|
||||
})
|
||||
return (
|
||||
getCanonicalPageIdImpl(pageId, recordMap, {
|
||||
uuid
|
||||
}) ?? undefined
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ if (!rawSiteConfig) {
|
||||
}
|
||||
|
||||
// allow environment variables to override site.config.ts
|
||||
let siteConfigOverrides: SiteConfig
|
||||
let siteConfigOverrides: SiteConfig | undefined
|
||||
|
||||
try {
|
||||
if (process.env.NEXT_PUBLIC_SITE_CONFIG) {
|
||||
siteConfigOverrides = JSON.parse(process.env.NEXT_PUBLIC_SITE_CONFIG)
|
||||
siteConfigOverrides = JSON.parse(
|
||||
process.env.NEXT_PUBLIC_SITE_CONFIG
|
||||
) as SiteConfig
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Invalid config "NEXT_PUBLIC_SITE_CONFIG" failed to parse')
|
||||
@@ -22,25 +24,36 @@ const siteConfig: SiteConfig = {
|
||||
...siteConfigOverrides
|
||||
}
|
||||
|
||||
export function getSiteConfig<T>(key: string, defaultValue?: T): T {
|
||||
const value = siteConfig[key]
|
||||
export function getSiteConfig<T, TDefault>(
|
||||
key: string,
|
||||
defaultValue?: TDefault
|
||||
): TDefault extends undefined ? T | undefined : T {
|
||||
const value = siteConfig[key as keyof SiteConfig]
|
||||
|
||||
if (value !== undefined) {
|
||||
return value
|
||||
return value as T
|
||||
}
|
||||
|
||||
if (defaultValue !== undefined) {
|
||||
return defaultValue
|
||||
return defaultValue as TDefault extends undefined ? T | undefined : T
|
||||
}
|
||||
|
||||
export function getRequiredSiteConfig<T>(key: string): T {
|
||||
const value = siteConfig[key as keyof SiteConfig]
|
||||
|
||||
if (value !== undefined) {
|
||||
return value as T
|
||||
}
|
||||
|
||||
throw new Error(`Config error: missing required site config value "${key}"`)
|
||||
}
|
||||
|
||||
export const isServer = typeof window === 'undefined'
|
||||
|
||||
export function getEnv(
|
||||
key: string,
|
||||
defaultValue?: string,
|
||||
env = process.env
|
||||
): string {
|
||||
): string | undefined {
|
||||
const value = env[key]
|
||||
|
||||
if (value !== undefined) {
|
||||
@@ -51,5 +64,7 @@ export function getEnv(
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
throw new Error(`Config error: missing required env variable "${key}"`)
|
||||
if (isServer) {
|
||||
throw new Error(`Config error: missing required env variable "${key}"`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const uuid = !!includeNotionIdInUrls
|
||||
export async function getSiteMap(): Promise<types.SiteMap> {
|
||||
const partialSiteMap = await getAllPages(
|
||||
config.rootNotionPageId,
|
||||
config.rootNotionSpaceId
|
||||
config.rootNotionSpaceId ?? undefined
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -25,14 +25,19 @@ const getAllPages = pMemoize(getAllPagesImpl, {
|
||||
cacheKey: (...args) => JSON.stringify(args)
|
||||
})
|
||||
|
||||
const getPage = async (pageId: string, ...args) => {
|
||||
const getPage = async (pageId: string, opts?: any) => {
|
||||
console.log('\nnotion getPage', uuidToId(pageId))
|
||||
return notion.getPage(pageId, ...args)
|
||||
return notion.getPage(pageId, {
|
||||
kyOptions: {
|
||||
timeout: 30_000
|
||||
},
|
||||
...opts
|
||||
})
|
||||
}
|
||||
|
||||
async function getAllPagesImpl(
|
||||
rootNotionPageId: string,
|
||||
rootNotionSpaceId: string
|
||||
rootNotionSpaceId?: string
|
||||
): Promise<Partial<types.SiteMap>> {
|
||||
const pageMap = await getAllPagesInSpace(
|
||||
rootNotionPageId,
|
||||
@@ -41,7 +46,7 @@ async function getAllPagesImpl(
|
||||
)
|
||||
|
||||
const canonicalPageMap = Object.keys(pageMap).reduce(
|
||||
(map, pageId: string) => {
|
||||
(map: Record<string, string>, pageId: string) => {
|
||||
const recordMap = pageMap[pageId]
|
||||
if (!recordMap) {
|
||||
throw new Error(`Error loading page "${pageId}"`)
|
||||
@@ -49,14 +54,14 @@ async function getAllPagesImpl(
|
||||
|
||||
const block = recordMap.block[pageId]?.value
|
||||
if (
|
||||
!(getPageProperty<boolean | null>('Public', block, recordMap) ?? true)
|
||||
!(getPageProperty<boolean | null>('Public', block!, recordMap) ?? true)
|
||||
) {
|
||||
return map
|
||||
}
|
||||
|
||||
const canonicalPageId = getCanonicalPageId(pageId, recordMap, {
|
||||
uuid
|
||||
})
|
||||
})!
|
||||
|
||||
if (map[canonicalPageId]) {
|
||||
// you can have multiple pages in different collections that have the same id
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { api, host } from './config'
|
||||
|
||||
export function getSocialImageUrl(pageId: string) {
|
||||
export function getSocialImageUrl(pageId: string | undefined) {
|
||||
try {
|
||||
const url = new URL(api.getSocialImage, host)
|
||||
|
||||
@@ -8,7 +8,7 @@ export function getSocialImageUrl(pageId: string) {
|
||||
url.searchParams.set('id', pageId)
|
||||
return url.toString()
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
console.warn('error invalid social image url', pageId, err.message)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async function getTweetImpl(tweetId: string): Promise<any> {
|
||||
if (cachedTweet || cachedTweet === null) {
|
||||
return cachedTweet
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error get "${cacheKey}"`, err.message)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ async function getTweetImpl(tweetId: string): Promise<any> {
|
||||
|
||||
try {
|
||||
await db.set(cacheKey, tweetData)
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error set "${cacheKey}"`, err.message)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const uuid = !!includeNotionIdInUrls
|
||||
export const mapPageUrl =
|
||||
(site: Site, recordMap: ExtendedRecordMap, searchParams: URLSearchParams) =>
|
||||
(pageId = '') => {
|
||||
const pageUuid = parsePageId(pageId, { uuid: true })
|
||||
const pageUuid = parsePageId(pageId, { uuid: true })!
|
||||
|
||||
if (uuidToId(pageUuid) === site.rootNotionPageId) {
|
||||
return createUrl('/', searchParams)
|
||||
@@ -27,7 +27,7 @@ export const mapPageUrl =
|
||||
export const getCanonicalPageUrl =
|
||||
(site: Site, recordMap: ExtendedRecordMap) =>
|
||||
(pageId = '') => {
|
||||
const pageUuid = parsePageId(pageId, { uuid: true })
|
||||
const pageUuid = parsePageId(pageId, { uuid: true })!
|
||||
|
||||
if (uuidToId(pageId) === site.rootNotionPageId) {
|
||||
return `https://${site.domain}`
|
||||
|
||||
@@ -19,7 +19,7 @@ import { getPreviewImageMap } from './preview-images'
|
||||
const getNavigationLinkPages = pMemoize(
|
||||
async (): Promise<ExtendedRecordMap[]> => {
|
||||
const navigationLinkPageIds = (navigationLinks || [])
|
||||
.map((link) => link.pageId)
|
||||
.map((link) => link?.pageId)
|
||||
.filter(Boolean)
|
||||
|
||||
if (navigationStyle !== 'default' && navigationLinkPageIds.length) {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const oembed = async ({
|
||||
dark?: boolean
|
||||
}) => {
|
||||
// TODO: handle pages with no pageId via domain
|
||||
const pageId = parsePageId(url)
|
||||
const pageId = parsePageId(url)!
|
||||
|
||||
let title = config.name
|
||||
let authorName = config.author
|
||||
@@ -26,7 +26,7 @@ export const oembed = async ({
|
||||
const pageTitle = getPageTitle(page)
|
||||
if (pageTitle) title = pageTitle
|
||||
|
||||
const user = page.notion_user[Object.keys(page.notion_user)[0]]?.value
|
||||
const user = page.notion_user[Object.keys(page.notion_user)[0]!]!.value
|
||||
const name = [user.given_name, user.family_name]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function getPreviewImageMap(
|
||||
const urls: string[] = getPageImageUrls(recordMap, {
|
||||
mapImageUrl
|
||||
})
|
||||
.concat([defaultPageIcon, defaultPageCover])
|
||||
.concat([defaultPageIcon, defaultPageCover].filter(Boolean))
|
||||
.filter(Boolean)
|
||||
|
||||
const previewImagesMap = Object.fromEntries(
|
||||
@@ -48,7 +48,7 @@ async function createPreviewImage(
|
||||
if (cachedPreviewImage) {
|
||||
return cachedPreviewImage
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error get "${cacheKey}"`, err.message)
|
||||
}
|
||||
@@ -65,13 +65,13 @@ async function createPreviewImage(
|
||||
|
||||
try {
|
||||
await db.set(cacheKey, previewImage)
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error set "${cacheKey}"`, err.message)
|
||||
}
|
||||
|
||||
return previewImage
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
console.warn('failed to create preview image', url, err.message)
|
||||
return null
|
||||
}
|
||||
|
||||
1
lib/reset.d.ts
vendored
Normal file
1
lib/reset.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import '@fisch0920/config/ts-reset'
|
||||
@@ -1,18 +1,22 @@
|
||||
import { type ExtendedRecordMap } from 'notion-types'
|
||||
import { parsePageId } from 'notion-utils'
|
||||
|
||||
import type { PageProps } from './types'
|
||||
import * as acl from './acl'
|
||||
import { environment, pageUrlAdditions, pageUrlOverrides, site } from './config'
|
||||
import { db } from './db'
|
||||
import { getSiteMap } from './get-site-map'
|
||||
import { getPage } from './notion'
|
||||
|
||||
export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
let pageId: string
|
||||
export async function resolveNotionPage(
|
||||
domain: string,
|
||||
rawPageId?: string
|
||||
): Promise<PageProps> {
|
||||
let pageId: string | undefined
|
||||
let recordMap: ExtendedRecordMap
|
||||
|
||||
if (rawPageId && rawPageId !== 'index') {
|
||||
pageId = parsePageId(rawPageId)
|
||||
pageId = parsePageId(rawPageId)!
|
||||
|
||||
if (!pageId) {
|
||||
// check if the site configuration provides an override or a fallback for
|
||||
@@ -21,7 +25,7 @@ export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
pageUrlOverrides[rawPageId] || pageUrlAdditions[rawPageId]
|
||||
|
||||
if (override) {
|
||||
pageId = parsePageId(override)
|
||||
pageId = parsePageId(override)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +41,7 @@ export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
pageId = await db.get(cacheKey)
|
||||
|
||||
// console.log(`redis get "${cacheKey}"`, pageId)
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error get "${cacheKey}"`, err.message)
|
||||
}
|
||||
@@ -64,7 +68,7 @@ export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
await db.set(cacheKey, pageId, cacheTTL)
|
||||
|
||||
// console.log(`redis set "${cacheKey}"`, pageId, { cacheTTL })
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// ignore redis errors
|
||||
console.warn(`redis error set "${cacheKey}"`, err.message)
|
||||
}
|
||||
@@ -86,6 +90,6 @@ export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
recordMap = await getPage(pageId)
|
||||
}
|
||||
|
||||
const props = { site, recordMap, pageId }
|
||||
const props: PageProps = { site, recordMap, pageId }
|
||||
return { ...props, ...(await acl.pageAcl(props)) }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ async function searchNotionImpl(
|
||||
error.response = res
|
||||
throw error
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => res.json() as Promise<types.SearchResults>)
|
||||
|
||||
// return ky
|
||||
// .post(api.searchNotion, {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type * as types from './types'
|
||||
|
||||
export interface SiteConfig {
|
||||
rootNotionPageId: string
|
||||
rootNotionSpaceId?: string
|
||||
rootNotionSpaceId?: string | null
|
||||
|
||||
name: string
|
||||
domain: string
|
||||
@@ -28,8 +28,8 @@ export interface SiteConfig {
|
||||
isSearchEnabled?: boolean
|
||||
|
||||
includeNotionIdInUrls?: boolean
|
||||
pageUrlOverrides?: types.PageUrlOverridesMap
|
||||
pageUrlAdditions?: types.PageUrlOverridesMap
|
||||
pageUrlOverrides?: types.PageUrlOverridesMap | null
|
||||
pageUrlAdditions?: types.PageUrlOverridesMap | null
|
||||
|
||||
navigationStyle?: types.NavigationStyle
|
||||
navigationLinks?: Array<NavigationLink>
|
||||
|
||||
12
lib/types.ts
12
lib/types.ts
@@ -31,7 +31,7 @@ export interface Site {
|
||||
domain: string
|
||||
|
||||
rootNotionPageId: string
|
||||
rootNotionSpaceId: string
|
||||
rootNotionSpaceId: string | null
|
||||
|
||||
// settings
|
||||
html?: string
|
||||
@@ -69,9 +69,9 @@ export interface PageUrlOverridesInverseMap {
|
||||
export interface NotionPageInfo {
|
||||
pageId: string
|
||||
title: string
|
||||
image: string
|
||||
imageObjectPosition: string
|
||||
author: string
|
||||
authorImage: string
|
||||
detail: string
|
||||
image?: string
|
||||
imageObjectPosition?: string
|
||||
author?: string
|
||||
authorImage?: string
|
||||
detail?: string
|
||||
}
|
||||
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import bundleAnalyzer from '@next/bundle-analyzer'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import bundleAnalyzer from '@next/bundle-analyzer'
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
// eslint-disable-next-line no-process-env
|
||||
enabled: process.env.ANALYZE === 'true'
|
||||
})
|
||||
|
||||
@@ -22,7 +24,7 @@ export default withBundleAnalyzer({
|
||||
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
|
||||
},
|
||||
|
||||
webpack: (config, _context) => {
|
||||
webpack: (config) => {
|
||||
// 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
|
||||
|
||||
56
package.json
56
package.json
@@ -6,6 +6,7 @@
|
||||
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||
"repository": "transitive-bullshit/nextjs-notion-starter-kit",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.11.1",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -37,38 +38,51 @@
|
||||
"classnames": "^2.5.1",
|
||||
"expiry-map": "^2.0.0",
|
||||
"fathom-client": "^3.4.1",
|
||||
"ky": "^1.7.2",
|
||||
"ky": "^1.8.1",
|
||||
"lqip-modern": "^2.2.1",
|
||||
"next": "^15.0.3",
|
||||
"notion-client": "^7.1.3",
|
||||
"notion-types": "^7.1.3",
|
||||
"notion-utils": "^7.1.3",
|
||||
"p-map": "^7.0.2",
|
||||
"next": "^15.3.3",
|
||||
"notion-client": "^7.4.0",
|
||||
"notion-types": "^7.4.0",
|
||||
"notion-utils": "^7.4.0",
|
||||
"p-map": "^7.0.3",
|
||||
"p-memoize": "^7.1.1",
|
||||
"posthog-js": "^1.181.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
"posthog-js": "^1.249.4",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "^19.1.0",
|
||||
"react-body-classname": "^1.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-notion-x": "^7.2.3",
|
||||
"react-tweet": "^3.2.1",
|
||||
"react-use": "^17.4.2",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-notion-x": "^7.4.0",
|
||||
"react-tweet": "^3.2.2",
|
||||
"react-use": "^17.6.0",
|
||||
"rss": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fisch0920/eslint-config": "^1.4.0",
|
||||
"@next/bundle-analyzer": "^15.0.2",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/react": "^18.0.21",
|
||||
"@fisch0920/config": "^1.1.2",
|
||||
"@next/bundle-analyzer": "^15.3.3",
|
||||
"@types/node": "^22.15.30",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^19.1.6",
|
||||
"@types/react-body-classname": "^1.1.10",
|
||||
"@types/rss": "^0.0.32",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^8.57.1",
|
||||
"npm-run-all2": "^7.0.1",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.6.3"
|
||||
"eslint": "^9.28.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"overrides": {
|
||||
"cacheable-request": {
|
||||
"keyv": "npm:@keyvhq/core@~1.6.6"
|
||||
}
|
||||
},
|
||||
"prettier": "@fisch0920/config/prettier",
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
"prettier --ignore-unknown --write",
|
||||
"eslint --fix"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { type PageProps, type Params } from '@/lib/types'
|
||||
export const getStaticProps: GetStaticProps<PageProps, Params> = async (
|
||||
context
|
||||
) => {
|
||||
const rawPageId = context.params.pageId as string
|
||||
const rawPageId = context.params?.pageId as string
|
||||
|
||||
try {
|
||||
const props = await resolveNotionPage(domain, rawPageId)
|
||||
@@ -48,6 +48,6 @@ export async function getStaticPaths() {
|
||||
return staticPaths
|
||||
}
|
||||
|
||||
export default function NotionDomainDynamicPage(props) {
|
||||
export default function NotionDomainDynamicPage(props: PageProps) {
|
||||
return <NotionPage {...props} />
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ 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 { posthog } from 'posthog-js'
|
||||
import * as React from 'react'
|
||||
|
||||
import { bootstrap } from '@/lib/bootstrap-client'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { IconContext } from '@react-icons/all-files'
|
||||
import Document, { Head, Html, Main, NextScript } from 'next/document'
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
render() {
|
||||
override render() {
|
||||
return (
|
||||
<IconContext.Provider value={{ style: { verticalAlign: 'middle' } }}>
|
||||
<Html lang='en'>
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function OGImage(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
const { searchParams } = new URL(req.url!)
|
||||
const pageId = parsePageId(
|
||||
searchParams.get('id') || libConfig.rootNotionPageId
|
||||
)
|
||||
@@ -178,7 +178,7 @@ export async function getNotionPageInfo({
|
||||
const recordMap = await notion.getPage(pageId)
|
||||
|
||||
const keys = Object.keys(recordMap?.block || {})
|
||||
const block = recordMap?.block?.[keys[0]]?.value
|
||||
const block = recordMap?.block?.[keys[0]!]?.value
|
||||
|
||||
if (!block) {
|
||||
throw new Error('Invalid recordMap for page')
|
||||
@@ -209,7 +209,7 @@ export async function getNotionPageInfo({
|
||||
libConfig.defaultPageCoverPosition
|
||||
const imageObjectPosition = imageCoverPosition
|
||||
? `center ${(1 - imageCoverPosition) * 100}%`
|
||||
: null
|
||||
: undefined
|
||||
|
||||
const imageBlockUrl = mapImageUrl(
|
||||
getPageProperty<string>('Social Image', block, recordMap) ||
|
||||
@@ -220,7 +220,7 @@ export async function getNotionPageInfo({
|
||||
|
||||
const blockIcon = getBlockIcon(block, recordMap)
|
||||
const authorImageBlockUrl = mapImageUrl(
|
||||
blockIcon && isUrl(blockIcon) ? blockIcon : null,
|
||||
blockIcon && isUrl(blockIcon) ? blockIcon : undefined,
|
||||
block
|
||||
)
|
||||
const authorImageFallbackUrl = mapImageUrl(libConfig.defaultPageIcon, block)
|
||||
@@ -272,7 +272,9 @@ export async function getNotionPageInfo({
|
||||
}
|
||||
}
|
||||
|
||||
async function isUrlReachable(url: string | null): Promise<boolean> {
|
||||
async function isUrlReachable(
|
||||
url: string | undefined | null
|
||||
): Promise<boolean> {
|
||||
if (!url) {
|
||||
return false
|
||||
}
|
||||
@@ -286,9 +288,9 @@ async function isUrlReachable(url: string | null): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function getCompatibleImageUrl(
|
||||
url: string | null,
|
||||
fallbackUrl: string | null
|
||||
): Promise<string | null> {
|
||||
url: string | undefined | null,
|
||||
fallbackUrl: string | undefined | null
|
||||
): Promise<string | undefined> {
|
||||
const image = (await isUrlReachable(url)) ? url : fallbackUrl
|
||||
|
||||
if (image) {
|
||||
@@ -303,5 +305,5 @@ async function getCompatibleImageUrl(
|
||||
}
|
||||
}
|
||||
|
||||
return image
|
||||
return image ?? undefined
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
||||
})
|
||||
|
||||
for (const pagePath of Object.keys(siteMap.canonicalPageMap)) {
|
||||
const pageId = siteMap.canonicalPageMap[pagePath]
|
||||
const pageId = siteMap.canonicalPageMap[pagePath]!
|
||||
const recordMap = siteMap.pageMap[pageId] as ExtendedRecordMap
|
||||
if (!recordMap) continue
|
||||
|
||||
const keys = Object.keys(recordMap?.block || {})
|
||||
const block = recordMap?.block?.[keys[0]]?.value
|
||||
const block = recordMap?.block?.[keys[0]!]?.value
|
||||
if (!block) continue
|
||||
|
||||
const parentPage = getBlockParentPage(block, recordMap)
|
||||
@@ -67,7 +67,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
||||
? new Date(lastUpdatedTime)
|
||||
: publishedTime
|
||||
? new Date(publishedTime)
|
||||
: undefined
|
||||
: new Date()
|
||||
const socialImageUrl = getSocialImageUrl(pageId)
|
||||
|
||||
feed.item({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { PageProps } from '@/lib/types'
|
||||
import { NotionPage } from '@/components/NotionPage'
|
||||
import { domain } from '@/lib/config'
|
||||
import { resolveNotionPage } from '@/lib/resolve-notion-page'
|
||||
@@ -16,6 +17,6 @@ export const getStaticProps = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
export default function NotionDomainPage(props) {
|
||||
export default function NotionDomainPage(props: PageProps) {
|
||||
return <NotionPage {...props} />
|
||||
}
|
||||
|
||||
3779
pnpm-lock.yaml
generated
3779
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,13 @@
|
||||
{
|
||||
"extends": "@fisch0920/config/tsconfig-react",
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/components/*": ["components/*"],
|
||||
"@/lib/*": ["lib/*"],
|
||||
"@/styles/*": ["styles/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "site.config.ts"]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "*.config.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user