diff --git a/packages/browser-types/tsconfig.json b/packages/browser-types/tsconfig.json index 92db2d902..c0b0f9326 100644 --- a/packages/browser-types/tsconfig.json +++ b/packages/browser-types/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": false, "declaration": true, "outDir": "dist", diff --git a/packages/cache-tags/src/index.ts b/packages/cache-tags/src/index.ts index f4350b717..072d7f00a 100644 --- a/packages/cache-tags/src/index.ts +++ b/packages/cache-tags/src/index.ts @@ -121,17 +121,6 @@ export function getCacheTag( } } -/** - * Get the cache tag for a given URL. - */ -export function getCacheTagForURL(url: string | URL) { - const parsedURL = url instanceof URL ? url : new URL(url); - return getCacheTag({ - tag: 'url', - hostname: parsedURL.hostname, - }); -} - /** * Get the tags for a computed content source. */ @@ -182,7 +171,7 @@ export function getComputedContentSourceCacheTags( break; default: // Do not throw for unknown dependency types - // as it might mean we are lacking behind the API version + // as it might mean we are lagging behind the API version break; } }); @@ -198,9 +187,8 @@ export function getComputedContentSourceCacheTags( } // We invalidate the computed content when a new version of the integration is deployed. - if (source.type.startsWith('integration:')) { - const integration = source.type.split(':')[1]; + const integration = source.type.split(':')[1]!; tags.push( getCacheTag({ tag: 'integration', diff --git a/packages/cache-tags/tsconfig.json b/packages/cache-tags/tsconfig.json index 92db2d902..c0b0f9326 100644 --- a/packages/cache-tags/tsconfig.json +++ b/packages/cache-tags/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": false, "declaration": true, "outDir": "dist", diff --git a/packages/colors/src/transformations.ts b/packages/colors/src/transformations.ts index 8234a460a..82d09e011 100644 --- a/packages/colors/src/transformations.ts +++ b/packages/colors/src/transformations.ts @@ -8,7 +8,7 @@ type RGBColor = [number, number, number]; type OKLABColor = { L: number; A: number; B: number }; type OKLCHColor = { L: number; C: number; H: number }; -const D65 = [95.047, 100.0, 108.883]; // Reference white (D65) +const D65 = [95.047, 100.0, 108.883] as const; // Reference white (D65) export enum ColorCategory { backgrounds = 'backgrounds', @@ -211,8 +211,8 @@ export function colorScale( const result = []; for (let index = 0; index < mapping.length; index++) { - const targetL = - foregroundColor.L * mapping[index] + backgroundColor.L * (1 - mapping[index]); + const step = mapping[index]!; + const targetL = foregroundColor.L * step + backgroundColor.L * (1 - step); if ( index === 8 && @@ -295,7 +295,7 @@ export function rgbArrayToHex(rgb: RGBColor): string { export function getColor(percentage: number, start: RGBColor, end: RGBColor) { const rgb = end.map((channel, index) => { - return Math.round(channel + percentage * (start[index] - channel)); + return Math.round(channel + percentage * (start[index]! - channel)); }); return rgbArrayToHex(rgb as RGBColor); @@ -392,14 +392,14 @@ export function xyzToLab65(xyz: [number, number, number]): { B: number; } { const [x, y, z] = xyz.map((v, i) => { - const scaled = v / D65[i]; + const scaled = v / D65[i]!; return scaled > 0.008856 ? Math.cbrt(scaled) : 7.787 * scaled + 16 / 116; }); return { - L: 116 * y - 16, - A: 500 * (x - y), - B: 200 * (y - z), + L: 116 * y! - 16, + A: 500 * (x! - y!), + B: 200 * (y! - z!), }; } diff --git a/packages/colors/tsconfig.json b/packages/colors/tsconfig.json index 92db2d902..c0b0f9326 100644 --- a/packages/colors/tsconfig.json +++ b/packages/colors/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": false, "declaration": true, "outDir": "dist", diff --git a/packages/fonts/src/getDefaultFont.ts b/packages/fonts/src/getDefaultFont.ts index ea18e8bba..d5ffa48df 100644 --- a/packages/fonts/src/getDefaultFont.ts +++ b/packages/fonts/src/getDefaultFont.ts @@ -67,7 +67,7 @@ function getBestUnicodeRange(text: string, ranges: Record): stri const body = token.slice(2); // drop "U+" const [startHex, endHex] = body.split('-'); - const start = Number.parseInt(startHex, 16); + const start = Number.parseInt(startHex!, 16); const end = endHex ? Number.parseInt(endHex, 16) : start; if (Number.isNaN(start) || Number.isNaN(end) || end < start) return null; @@ -92,7 +92,7 @@ function getBestUnicodeRange(text: string, ranges: Record): stri for (const [label, rangesArr] of Object.entries(parsed)) { if (rangesArr.some(([lo, hi]) => cp >= lo && cp <= hi)) { - hits[label]++; + hits[label]!++; } } } diff --git a/packages/fonts/tsconfig.json b/packages/fonts/tsconfig.json index 2b3fe87c5..201ca0989 100644 --- a/packages/fonts/tsconfig.json +++ b/packages/fonts/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "target": "es2022", "strict": true, + "noUncheckedIndexedAccess": true, "esModuleInterop": true, "skipLibCheck": true, "moduleResolution": "bundler", diff --git a/packages/gitbook/openNext/customWorkers/script/updateWrangler.ts b/packages/gitbook/openNext/customWorkers/script/updateWrangler.ts index 0fdbf6cc7..738ff7df1 100644 --- a/packages/gitbook/openNext/customWorkers/script/updateWrangler.ts +++ b/packages/gitbook/openNext/customWorkers/script/updateWrangler.ts @@ -11,7 +11,7 @@ const args = process.argv.slice(2); const versionId = args[0]; // The preview URL is in the format https://-gitbook-open-v2-server-preview.gitbook.workers.dev -const previewHostname = `${versionId.split('-')[0]}-gitbook-open-v2-server-preview.gitbook.workers.dev`; +const previewHostname = `${versionId?.split('-')[0]}-gitbook-open-v2-server-preview.gitbook.workers.dev`; let updatedFile = file.replace( /"PREVIEW_HOSTNAME": "TO_REPLACE"/, diff --git a/packages/gitbook/src/components/AI/server-actions/api.tsx b/packages/gitbook/src/components/AI/server-actions/api.tsx index 99b7424f1..23440a6ae 100644 --- a/packages/gitbook/src/components/AI/server-actions/api.tsx +++ b/packages/gitbook/src/components/AI/server-actions/api.tsx @@ -39,7 +39,9 @@ export async function streamRenderAIMessage( if (message.steps[stepIndex]) { message.steps = [...message.steps]; + // @ts-expect-error message.steps[stepIndex] = { ...message.steps[stepIndex] }; + // @ts-expect-error callback(message.steps[stepIndex]); } else { message.steps = [ @@ -52,6 +54,7 @@ export async function streamRenderAIMessage( }, }, ]; + // @ts-expect-error callback(message.steps[stepIndex]); } }; diff --git a/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx b/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx index 5ac3c1e9c..dc175cc1b 100644 --- a/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx +++ b/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx @@ -111,14 +111,15 @@ function DefaultAction(props: AIActionsDropdownProps) { (assistant) => assistant.ui === true && assistant.pageAction ); - if (assistants.length) { - return ; + const assistant = assistants[0]; + if (assistant) { + return ; } if (actions.markdown) { return ( diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts b/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts index 650fbd1f6..315d072e6 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/highlight.ts @@ -119,7 +119,7 @@ export async function highlight( currentIndex += 1; // for the \n return { - highlighted: Boolean(lineBlock.data.highlighted), + highlighted: Boolean(lineBlock?.data.highlighted), tokens: result, }; }); diff --git a/packages/gitbook/src/components/DocumentView/Hint.tsx b/packages/gitbook/src/components/DocumentView/Hint.tsx index 3c891d819..17181ad06 100644 --- a/packages/gitbook/src/components/DocumentView/Hint.tsx +++ b/packages/gitbook/src/components/DocumentView/Hint.tsx @@ -7,13 +7,16 @@ import { Block, type BlockProps } from './Block'; import { Blocks } from './Blocks'; import { getBlockTextStyle } from './spacing'; -export function Hint(props: BlockProps) { - const { block, style, ancestorBlocks, ...contextProps } = props; +export function Hint({ + block, + style, + ancestorBlocks, + ...contextProps +}: BlockProps) { const hintStyle = HINT_STYLES[block.data.style] ?? HINT_STYLES.info; - const firstLine = getBlockTextStyle(block.nodes[0]); - - const firstNode = block.nodes[0]; - const hasHeading = ['heading-1', 'heading-2', 'heading-3'].includes(block.nodes[0].type); + const firstNode = block.nodes[0]!; + const firstLine = getBlockTextStyle(firstNode); + const hasHeading = ['heading-1', 'heading-2', 'heading-3'].includes(firstNode.type); return (
) { const parent = ancestorBlocks[ancestorBlocks.length - 1]; assert( - (parent && parent.type === 'list-ordered') || - parent.type === 'list-unordered' || - parent.type === 'list-tasks', + parent?.type === 'list-ordered' || + parent?.type === 'list-unordered' || + parent?.type === 'list-tasks', 'Invalid parent list type' ); @@ -112,11 +112,11 @@ function getListItemDepth(input: { for (let i = ancestorBlocks.length - 1; i >= 0; i--) { const block = ancestorBlocks[i]; - if (block.type === type) { + if (block?.type === type) { depth = depth + 1; continue; } - if (block.type === 'list-item') { + if (block?.type === 'list-item') { continue; } break; diff --git a/packages/gitbook/src/components/DocumentView/StepperStep.tsx b/packages/gitbook/src/components/DocumentView/StepperStep.tsx index c31e3484c..452e863b1 100644 --- a/packages/gitbook/src/components/DocumentView/StepperStep.tsx +++ b/packages/gitbook/src/components/DocumentView/StepperStep.tsx @@ -10,7 +10,7 @@ export function StepperStep(props: BlockProps) { const { block, style, ancestorBlocks, ...contextProps } = props; const ancestor = ancestorBlocks[ancestorBlocks.length - 1]; - assert(ancestor.type === 'stepper', 'Ancestor block must be a stepper'); + assert(ancestor?.type === 'stepper', 'Ancestor block must be a stepper'); const index = ancestor.nodes.indexOf(block); diff --git a/packages/gitbook/src/components/DocumentView/Table/RecordRow.tsx b/packages/gitbook/src/components/DocumentView/Table/RecordRow.tsx index b09760ceb..ccadb8391 100644 --- a/packages/gitbook/src/components/DocumentView/Table/RecordRow.tsx +++ b/packages/gitbook/src/components/DocumentView/Table/RecordRow.tsx @@ -26,6 +26,7 @@ export function RecordRow( autoSizedColumns, fixedColumns, }); + // @ts-expect-error const verticalAlignment = getColumnVerticalAlignment(block.data.definition[column]); return ( diff --git a/packages/gitbook/src/components/DocumentView/Table/ViewGrid.tsx b/packages/gitbook/src/components/DocumentView/Table/ViewGrid.tsx index 606b2305b..0ff9584df 100644 --- a/packages/gitbook/src/components/DocumentView/Table/ViewGrid.tsx +++ b/packages/gitbook/src/components/DocumentView/Table/ViewGrid.tsx @@ -25,7 +25,9 @@ export function ViewGrid(props: TableViewProps) { /* Only show the header when configured and not empty */ const withHeader = !view.hideHeader && - view.columns.some((columnId) => block.data.definition[columnId].title.trim().length > 0); + view.columns.some( + (columnId) => (block.data.definition[columnId]?.title.trim().length ?? 0) > 0 + ); return (
@@ -43,28 +45,31 @@ export function ViewGrid(props: TableViewProps) { )} >
- {view.columns.map((column) => ( -
- {block.data.definition[column].title} -
- ))} + {view.columns.map((column) => { + const definition = block.data.definition[column]!; + return ( +
+ {definition.title} +
+ ); + })}
)} diff --git a/packages/gitbook/src/components/DocumentView/Tabs/DynamicTabs.tsx b/packages/gitbook/src/components/DocumentView/Tabs/DynamicTabs.tsx index ab4d3d5b9..f97df5bc7 100644 --- a/packages/gitbook/src/components/DocumentView/Tabs/DynamicTabs.tsx +++ b/packages/gitbook/src/components/DocumentView/Tabs/DynamicTabs.tsx @@ -206,7 +206,7 @@ export function DynamicTabs( 'max-w-full', 'truncate', - active.id === tab.id + active?.id === tab.id ? [ 'shrink-0', 'active-tab', @@ -222,7 +222,7 @@ export function DynamicTabs(
diff --git a/packages/gitbook/src/components/DocumentView/spacing.ts b/packages/gitbook/src/components/DocumentView/spacing.ts index 2cd35bdba..b77d18cb9 100644 --- a/packages/gitbook/src/components/DocumentView/spacing.ts +++ b/packages/gitbook/src/components/DocumentView/spacing.ts @@ -43,9 +43,9 @@ export function getBlockTextStyle(block: DocumentBlock): { case 'list-ordered': case 'list-tasks': case 'list-unordered': - return getBlockTextStyle(block.nodes[0]); + return getBlockTextStyle(block.nodes[0]!); case 'list-item': - return getBlockTextStyle(block.nodes[0]); + return getBlockTextStyle(block.nodes[0]!); default: return { textSize: 'text-base', diff --git a/packages/gitbook/src/components/DocumentView/utils/isBlockOffscreen.ts b/packages/gitbook/src/components/DocumentView/utils/isBlockOffscreen.ts index 522a47d2b..b24ead624 100644 --- a/packages/gitbook/src/components/DocumentView/utils/isBlockOffscreen.ts +++ b/packages/gitbook/src/components/DocumentView/utils/isBlockOffscreen.ts @@ -21,7 +21,7 @@ export function isBlockOffscreen( const allAncestors = [document, ...ancestorBlocks]; for (let index = allAncestors.length - 1; index >= 0; index--) { - const parent = allAncestors[index]; + const parent = allAncestors[index]!; const offset = getBlockOffset({ block: current, parent }); if (offset > screenHeight) { diff --git a/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx b/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx index 15feeb750..103082ce8 100644 --- a/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx +++ b/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx @@ -19,14 +19,8 @@ const SECTION_INTERSECTING_THRESHOLD = 0.9; */ const ACTIVE_ITEM_OFFSET = 100; -export function ScrollSectionsList(props: { sections: DocumentSection[] }) { - const { sections } = props; - - const ids = React.useMemo(() => { - return sections.map((section) => { - return section.id; - }); - }, [sections]); +export function ScrollSectionsList({ sections }: { sections: DocumentSection[] }) { + const ids = React.useMemo(() => sections.map(({ id }) => id), [sections]); const enabled = useBodyLoaded(); diff --git a/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx b/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx index 1369ea86c..9f86cdf4f 100644 --- a/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx +++ b/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx @@ -140,7 +140,13 @@ export async function CustomizationRootLayout(props: { customization.styling.primaryColor.light ) }; - --header-link: ${hexToRgb(customization.header.linkColor?.light ?? colorContrast(tintColor?.light ?? customization.styling.primaryColor.light))}; + --header-link: ${hexToRgb( + // @ts-expect-error + customization.header.linkColor?.light ?? + colorContrast( + tintColor?.light ?? customization.styling.primaryColor.light + ) + )}; ${generateColorVariable('info', infoColor.light)} ${generateColorVariable('warning', warningColor.light)} @@ -154,7 +160,13 @@ export async function CustomizationRootLayout(props: { ${generateColorVariable('neutral', DEFAULT_TINT_COLOR, { darkMode: true })} --header-background: ${hexToRgb(customization.header.backgroundColor?.dark ?? tintColor?.dark ?? customization.styling.primaryColor.dark)}; - --header-link: ${hexToRgb(customization.header.linkColor?.dark ?? colorContrast(tintColor?.dark ?? customization.styling.primaryColor.dark))}; + --header-link: ${hexToRgb( + // @ts-expect-error + customization.header.linkColor?.dark ?? + colorContrast( + tintColor?.dark ?? customization.styling.primaryColor.dark + ) + )}; ${generateColorVariable('info', infoColor.dark, { darkMode: true })} ${generateColorVariable('warning', warningColor.dark, { darkMode: true })} @@ -325,6 +337,7 @@ function generateColorVariable( return Object.entries(shades) .map(([key, value]) => { const rgbValue = hexToRgb(value); // Check the original hex value + // @ts-expect-error const contrastValue = withContrast ? hexToRgb(colorContrast(value)) : undefined; // Add contrast if needed return `--${name}-${key}: ${rgbValue}; ${ contrastValue ? `--contrast-${name}-${key}: ${contrastValue};` : '' diff --git a/packages/gitbook/src/components/Search/SearchContainer.tsx b/packages/gitbook/src/components/Search/SearchContainer.tsx index 3e5e5e610..daa74ce51 100644 --- a/packages/gitbook/src/components/Search/SearchContainer.tsx +++ b/packages/gitbook/src/components/Search/SearchContainer.tsx @@ -55,7 +55,7 @@ export function SearchContainer(props: SearchContainerProps) { initialRef.current = true; // For simplicity we're only triggering the first assistant - assistants[0].open(state?.ask ?? undefined); + assistants[0]?.open(state?.ask ?? undefined); }, [state?.ask, assistants.length, assistants[0]?.open]); const onClose = React.useCallback( diff --git a/packages/gitbook/src/components/Search/SearchResults.tsx b/packages/gitbook/src/components/Search/SearchResults.tsx index 0c85596fb..687f31f38 100644 --- a/packages/gitbook/src/components/Search/SearchResults.tsx +++ b/packages/gitbook/src/components/Search/SearchResults.tsx @@ -296,7 +296,7 @@ export const SearchResults = React.forwardRef(function SearchResults( key={item.id} question={item.question} active={index === cursor} - assistant={assistants[0]} + assistant={assistants[0]!} recommended /> ); diff --git a/packages/gitbook/src/components/Search/server-actions.tsx b/packages/gitbook/src/components/Search/server-actions.tsx index 5e187b306..89e57a39f 100644 --- a/packages/gitbook/src/components/Search/server-actions.tsx +++ b/packages/gitbook/src/components/Search/server-actions.tsx @@ -71,8 +71,7 @@ export interface AskAnswerResult { export async function searchAllSiteContent(query: string): Promise { return traceErrorOnly('Search.searchAllSiteContent', async () => { const context = await getServerActionBaseContext(); - - return await searchSiteContent(context, { + return searchSiteContent(context, { query, scope: { mode: 'all' }, }); @@ -270,7 +269,7 @@ async function searchSiteContent( return ( await Promise.all( - searchResults.map(async (spaceItem) => { + searchResults.map((spaceItem) => { const found = findSiteSpaceBy( structure, (siteSpace) => siteSpace.space.id === spaceItem.id diff --git a/packages/gitbook/src/components/hooks/useHasBeenInViewport.ts b/packages/gitbook/src/components/hooks/useHasBeenInViewport.ts index 220ef6f91..91e2700db 100644 --- a/packages/gitbook/src/components/hooks/useHasBeenInViewport.ts +++ b/packages/gitbook/src/components/hooks/useHasBeenInViewport.ts @@ -25,7 +25,7 @@ export function useHasBeenInViewport( const observer = new IntersectionObserver( ([entry]) => { - if (entry.isIntersecting) { + if (entry?.isIntersecting) { setVisible(true); observer.disconnect(); } diff --git a/packages/gitbook/src/components/hooks/useInViewportListener.ts b/packages/gitbook/src/components/hooks/useInViewportListener.ts index 8f44fd826..199a6cb6b 100644 --- a/packages/gitbook/src/components/hooks/useInViewportListener.ts +++ b/packages/gitbook/src/components/hooks/useInViewportListener.ts @@ -30,8 +30,9 @@ export function useInViewportListener( const observer = new IntersectionObserver( ([entry]) => { - isIntersectingRef.current = entry.isIntersecting; - listenerRef.current(entry.isIntersecting, () => { + const isIntersecting = entry?.isIntersecting ?? false; + isIntersectingRef.current = isIntersecting; + listenerRef.current(isIntersecting, () => { observer.disconnect(); }); }, diff --git a/packages/gitbook/src/components/hooks/useScrollActiveId.ts b/packages/gitbook/src/components/hooks/useScrollActiveId.ts index adb28fe01..9d2ea7eb9 100644 --- a/packages/gitbook/src/components/hooks/useScrollActiveId.ts +++ b/packages/gitbook/src/components/hooks/useScrollActiveId.ts @@ -5,19 +5,22 @@ import React from 'react'; */ export function useScrollActiveId( ids: string[], - options: { + { + rootMargin, + threshold = 0.5, + enabled, + }: { rootMargin?: string; threshold?: number; enabled: boolean; } = { enabled: true } ) { - const { rootMargin, threshold = 0.5, enabled } = options; - - const [activeId, setActiveId] = React.useState(ids[0]); + const [activeId, setActiveId] = React.useState(ids[0]!); const sectionsIntersectingMap = React.useRef>(new Map()); React.useEffect(() => { const defaultActiveId = ids[0]; + // @ts-expect-error setActiveId((activeId) => (ids.indexOf(activeId) !== -1 ? activeId : defaultActiveId)); if (!enabled) { return; diff --git a/packages/gitbook/src/components/utils/ZoomImage.tsx b/packages/gitbook/src/components/utils/ZoomImage.tsx index 70a0adeb0..c0d74845e 100644 --- a/packages/gitbook/src/components/utils/ZoomImage.tsx +++ b/packages/gitbook/src/components/utils/ZoomImage.tsx @@ -41,8 +41,8 @@ export function ZoomImage( // Since the image is removed from the DOM when the modal is opened, // We only care when the size is defined. if (imgEntry && imgEntry.contentRect.width !== 0) { - viewWidth = entries[0]?.contentRect.width; - setPlaceholderRect(entries[0].contentRect); + viewWidth = imgEntry.contentRect.width; + setPlaceholderRect(imgEntry.contentRect); onChange(); } }) diff --git a/packages/gitbook/src/fonts/custom.test.ts b/packages/gitbook/src/fonts/custom.test.ts index 909c93960..60b52789e 100644 --- a/packages/gitbook/src/fonts/custom.test.ts +++ b/packages/gitbook/src/fonts/custom.test.ts @@ -3,201 +3,180 @@ import type { CustomizationFontDefinition } from '@gitbook/api'; import stylelint from 'stylelint'; import { generateFontFacesCSS, getFontSourcesToPreload } from './custom'; -const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = { - basic: { - id: 'open-sans', - custom: true, - fontFamily: 'Open Sans', - fontFaces: [ - { - weight: 400, - sources: [ - { - url: 'https://example.com/fonts/opensans-regular.woff2', - format: 'woff2', - }, - ], - }, - { - weight: 700, - sources: [ - { - url: 'https://example.com/fonts/opensans-bold.woff2', - format: 'woff2', - }, - ], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_BASIC: CustomizationFontDefinition = { + id: 'open-sans', + custom: true, + fontFamily: 'Open Sans', + fontFaces: [ + { + weight: 400, + sources: [ + { + url: 'https://example.com/fonts/opensans-regular.woff2', + format: 'woff2', + }, + ], }, + { + weight: 700, + sources: [ + { + url: 'https://example.com/fonts/opensans-bold.woff2', + format: 'woff2', + }, + ], + }, + ], + permissions: { + edit: false, }, +}; - multiWeight: { - id: 'roboto', - custom: true, - fontFamily: 'Roboto', - fontFaces: [ - { - weight: 300, - sources: [ - { - url: 'https://example.com/fonts/roboto-light.woff2', - format: 'woff2', - }, - ], - }, - { - weight: 400, - sources: [ - { - url: 'https://example.com/fonts/roboto-regular.woff2', - format: 'woff2', - }, - ], - }, - { - weight: 500, - sources: [ - { - url: 'https://example.com/fonts/roboto-medium.woff2', - format: 'woff2', - }, - ], - }, - { - weight: 700, - sources: [ - { - url: 'https://example.com/fonts/roboto-bold.woff2', - format: 'woff2', - }, - ], - }, - { - weight: 900, - sources: [ - { - url: 'https://example.com/fonts/roboto-black.woff2', - format: 'woff2', - }, - ], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_MULTI_WEIGHT: CustomizationFontDefinition = { + id: 'roboto', + custom: true, + fontFamily: 'Roboto', + fontFaces: [ + { + weight: 300, + sources: [ + { + url: 'https://example.com/fonts/roboto-light.woff2', + format: 'woff2', + }, + ], }, + { + weight: 400, + sources: [ + { + url: 'https://example.com/fonts/roboto-regular.woff2', + format: 'woff2', + }, + ], + }, + { + weight: 500, + sources: [ + { + url: 'https://example.com/fonts/roboto-medium.woff2', + format: 'woff2', + }, + ], + }, + { + weight: 700, + sources: [ + { + url: 'https://example.com/fonts/roboto-bold.woff2', + format: 'woff2', + }, + ], + }, + { + weight: 900, + sources: [ + { + url: 'https://example.com/fonts/roboto-black.woff2', + format: 'woff2', + }, + ], + }, + ], + permissions: { + edit: false, }, +}; - multiSource: { - id: 'lato', - custom: true, - fontFamily: 'Lato', - fontFaces: [ - { - weight: 400, - sources: [ - { - url: 'https://example.com/fonts/lato-regular.woff2', - format: 'woff2', - }, - { url: 'https://example.com/fonts/lato-regular.woff', format: 'woff' }, - ], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_MULTI_SOURCE: CustomizationFontDefinition = { + id: 'lato', + custom: true, + fontFamily: 'Lato', + fontFaces: [ + { + weight: 400, + sources: [ + { + url: 'https://example.com/fonts/lato-regular.woff2', + format: 'woff2', + }, + { url: 'https://example.com/fonts/lato-regular.woff', format: 'woff' }, + ], }, + ], + permissions: { + edit: false, }, +}; - missingFormat: { - id: 'source-sans', - custom: true, - fontFamily: 'Source Sans Pro', - fontFaces: [ - { - weight: 400, - sources: [ - { url: 'https://example.com/fonts/sourcesans-regular.woff2' }, - { - url: 'https://example.com/fonts/sourcesans-regular.woff', - format: 'woff', - }, - ], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_MISSING_FORMAT: CustomizationFontDefinition = { + id: 'source-sans', + custom: true, + fontFamily: 'Source Sans Pro', + fontFaces: [ + { + weight: 400, + sources: [ + { url: 'https://example.com/fonts/sourcesans-regular.woff2' }, + { + url: 'https://example.com/fonts/sourcesans-regular.woff', + format: 'woff', + }, + ], }, + ], + permissions: { + edit: false, }, +}; - empty: { - id: 'empty-font', - custom: true, - fontFamily: 'Empty Font', - fontFaces: [], - permissions: { - edit: false, - }, +const TEST_FONTS_EMPTY: CustomizationFontDefinition = { + id: 'empty-font', + custom: true, + fontFamily: 'Empty Font', + fontFaces: [], + permissions: { + edit: false, }, +}; - specialChars: { - id: 'special-font', - custom: true, - fontFamily: 'Special Font & Co.', - fontFaces: [ - { - weight: 400, - sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_SPECIAL_CHARS: CustomizationFontDefinition = { + id: 'special-font', + custom: true, + fontFamily: 'Special Font & Co.', + fontFaces: [ + { + weight: 400, + sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }], }, + ], + permissions: { + edit: false, }, +}; - complex: { - id: 'complex-font', - custom: true, - fontFamily: 'Complex Font', - fontFaces: [ - { - weight: 400, - sources: [ - { url: 'https://example.com/fonts/regular.woff2' }, - { url: 'https://example.com/fonts/regular.woff' }, - ], - }, - { - weight: 700, - sources: [ - { url: 'https://example.com/fonts/bold.woff2' }, - { url: 'https://example.com/fonts/bold.woff' }, - ], - }, - ], - permissions: { - edit: false, +const TEST_FONTS_COMPLEX: CustomizationFontDefinition = { + id: 'complex-font', + custom: true, + fontFamily: 'Complex Font', + fontFaces: [ + { + weight: 400, + sources: [ + { url: 'https://example.com/fonts/regular.woff2' }, + { url: 'https://example.com/fonts/regular.woff' }, + ], }, - }, - - variousURLs: { - id: 'various-urls', - custom: true, - fontFamily: 'Various URLs Font', - fontFaces: [ - { - weight: 400, - sources: [ - { url: 'https://example.com/fonts.woff2' }, - { url: 'https://example.com/fonts.woff' }, - { url: 'https://example.com/fonts.woff2' }, - ], - }, - ], - permissions: { - edit: false, + { + weight: 700, + sources: [ + { url: 'https://example.com/fonts/bold.woff2' }, + { url: 'https://example.com/fonts/bold.woff' }, + ], }, + ], + permissions: { + edit: false, }, }; @@ -229,7 +208,7 @@ async function isCSSValid(css: string): Promise { describe('generateFontFacesCSS', () => { test('basic case with regular and bold weights', async () => { - const css = generateFontFacesCSS(TEST_FONTS.basic, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_BASIC, 'content'); const isValid = await isCSSValid(css); expect(isValid).toBe(true); @@ -244,7 +223,7 @@ describe('generateFontFacesCSS', () => { }); test('mono type', async () => { - const css = generateFontFacesCSS(TEST_FONTS.basic, 'mono'); + const css = generateFontFacesCSS(TEST_FONTS_BASIC, 'mono'); const isValid = await isCSSValid(css); expect(isValid).toBe(true); @@ -259,7 +238,7 @@ describe('generateFontFacesCSS', () => { }); test('multiple font weights', async () => { - const css = generateFontFacesCSS(TEST_FONTS.multiWeight, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_MULTI_WEIGHT, 'content'); const isValid = await isCSSValid(css); expect(isValid).toBe(true); @@ -270,7 +249,7 @@ describe('generateFontFacesCSS', () => { }); test('multiple sources for a single weight', async () => { - const css = generateFontFacesCSS(TEST_FONTS.multiSource, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_MULTI_SOURCE, 'content'); const isValid = await isCSSValid(css); expect(isValid).toBe(true); @@ -281,7 +260,7 @@ describe('generateFontFacesCSS', () => { }); test('missing format property', async () => { - const css = generateFontFacesCSS(TEST_FONTS.missingFormat, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_MISSING_FORMAT, 'content'); const isValid = await isCSSValid(css); expect(isValid).toBe(true); @@ -292,13 +271,13 @@ describe('generateFontFacesCSS', () => { }); test('empty font faces array', async () => { - const css = generateFontFacesCSS(TEST_FONTS.empty, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_EMPTY, 'content'); expect(css).toBe(''); }); test('font with special characters in name', async () => { - const css = generateFontFacesCSS(TEST_FONTS.specialChars, 'content'); + const css = generateFontFacesCSS(TEST_FONTS_SPECIAL_CHARS, 'content'); // Validate CSS syntax const isValid = await isCSSValid(css); @@ -308,13 +287,13 @@ describe('generateFontFacesCSS', () => { describe('getFontSourcesToPreload', () => { const preloadTestCases = [ - { name: 'basic case', font: TEST_FONTS.basic, expectedCount: 2 }, - { name: 'multiple weights', font: TEST_FONTS.multiWeight, expectedCount: 2 }, - { name: 'multiple sources', font: TEST_FONTS.multiSource, expectedCount: 2 }, - { name: 'missing format', font: TEST_FONTS.missingFormat, expectedCount: 2 }, - { name: 'empty font faces', font: TEST_FONTS.empty, expectedCount: 0 }, - { name: 'special characters', font: TEST_FONTS.specialChars, expectedCount: 1 }, - { name: 'complex case', font: TEST_FONTS.complex, expectedCount: 4 }, + { name: 'basic case', font: TEST_FONTS_BASIC, expectedCount: 2 }, + { name: 'multiple weights', font: TEST_FONTS_MULTI_WEIGHT, expectedCount: 2 }, + { name: 'multiple sources', font: TEST_FONTS_MULTI_SOURCE, expectedCount: 2 }, + { name: 'missing format', font: TEST_FONTS_MISSING_FORMAT, expectedCount: 2 }, + { name: 'empty font faces', font: TEST_FONTS_EMPTY, expectedCount: 0 }, + { name: 'special characters', font: TEST_FONTS_SPECIAL_CHARS, expectedCount: 1 }, + { name: 'complex case', font: TEST_FONTS_COMPLEX, expectedCount: 4 }, ]; preloadTestCases.forEach(({ name, font, expectedCount }) => { diff --git a/packages/gitbook/src/intl/translate.tsx b/packages/gitbook/src/intl/translate.tsx index 79fc84e6d..a3285ef72 100644 --- a/packages/gitbook/src/intl/translate.tsx +++ b/packages/gitbook/src/intl/translate.tsx @@ -28,6 +28,7 @@ export function t( const [partToPush, partToReplace] = currentStringToReplace.split(`\${${i + 1}}`); parts.push({partToPush}); parts.push({arg}); + // @ts-expect-error currentStringToReplace = partToReplace; } }); diff --git a/packages/gitbook/src/lib/context.ts b/packages/gitbook/src/lib/context.ts index 85c0d3b2c..44147fa88 100644 --- a/packages/gitbook/src/lib/context.ts +++ b/packages/gitbook/src/lib/context.ts @@ -195,7 +195,7 @@ export async function fetchSiteContextByURLLookup( baseContext: GitBookBaseContext, data: SiteURLData ): Promise { - return await fetchSiteContextByIds(baseContext, { + return fetchSiteContextByIds(baseContext, { organization: data.organization, site: data.site, siteSection: data.siteSection, diff --git a/packages/gitbook/src/lib/data/urls.ts b/packages/gitbook/src/lib/data/urls.ts index 1c59f3260..e1780545d 100644 --- a/packages/gitbook/src/lib/data/urls.ts +++ b/packages/gitbook/src/lib/data/urls.ts @@ -111,7 +111,7 @@ export function getURLLookupAlternatives(input: URL) { // Mark the longuest entry to lookup as primary alternatives.sort((a, b) => b.extraPath.length - a.extraPath.length); if (alternatives.length > 0) { - alternatives[alternatives.length - 1].primary = true; + alternatives[alternatives.length - 1]!.primary = true; } return { urls: alternatives, basePath, changeRequest, revision }; diff --git a/packages/gitbook/src/lib/markdownPage.ts b/packages/gitbook/src/lib/markdownPage.ts index d3cccb27c..a16bd01cf 100644 --- a/packages/gitbook/src/lib/markdownPage.ts +++ b/packages/gitbook/src/lib/markdownPage.ts @@ -110,7 +110,7 @@ function isEmptyMarkdownPage(markdown: string): boolean { if ( node.type === 'paragraph' && node.children.length === 1 && - node.children[0].type === 'text' && + node.children[0]!.type === 'text' && !node.children[0].value.trim() ) { continue; diff --git a/packages/gitbook/src/lib/openapi/enrich.test.ts b/packages/gitbook/src/lib/openapi/enrich.test.ts index 6e91b1a83..0321b4fd8 100644 --- a/packages/gitbook/src/lib/openapi/enrich.test.ts +++ b/packages/gitbook/src/lib/openapi/enrich.test.ts @@ -12,7 +12,7 @@ describe('#enrichFilesystem', () => { rootURL: null, }); const enriched = await enrichFilesystem(filesystem); - expect(enriched[0].specification.paths['/pet'].put['x-gitbook-description-html']).toBe( + expect(enriched[0]?.specification.paths['/pet'].put['x-gitbook-description-html']).toBe( '

Social platform

' ); }); diff --git a/packages/gitbook/src/lib/preview.ts b/packages/gitbook/src/lib/preview.ts index 7094d1197..e0e2bd909 100644 --- a/packages/gitbook/src/lib/preview.ts +++ b/packages/gitbook/src/lib/preview.ts @@ -9,5 +9,5 @@ export function getPreviewRequestIdentifier(requestURL: URL): string { // For preview requests, we extract the site ID from the pathname // e.g. https://preview/site_id/... const pathname = requestURL.pathname.slice(1).split('/'); - return pathname[0]; + return pathname[0]!; } diff --git a/packages/gitbook/src/lib/visitors.ts b/packages/gitbook/src/lib/visitors.ts index ee3747fe7..540b758c3 100644 --- a/packages/gitbook/src/lib/visitors.ts +++ b/packages/gitbook/src/lib/visitors.ts @@ -157,7 +157,7 @@ export function getVisitorUnsignedClaims(args: { if (typeof parsed === 'object' && parsed !== null) { Object.assign(claims, parsed); } - } catch (_err) { + } catch { console.warn(`Invalid JSON in unsigned claim cookie "${cookie.name}"`); } } @@ -188,7 +188,7 @@ function setVisitorClaimByPath( let current = claims; for (let index = 0; index < keys.length; index++) { - const key = keys[index]; + const key = keys[index]!; if (index === keys.length - 1) { current[key] = value; diff --git a/packages/gitbook/src/middleware.ts b/packages/gitbook/src/middleware.ts index 0bedeef84..68adeb813 100644 --- a/packages/gitbook/src/middleware.ts +++ b/packages/gitbook/src/middleware.ts @@ -337,8 +337,6 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { pathname, ].join('/'); - console.log(`rewriting ${request.nextUrl.toString()} to ${route}`); - const rewrittenURL = new URL(`/${route}`, request.nextUrl.toString()); rewrittenURL.search = request.nextUrl.search; // Preserve the original search params @@ -541,7 +539,7 @@ function encodePathInSiteContent(rawPathname: string): { const embedPage = pathname.match(/^~gitbook\/embed\/page\/(\S+)$/); if (embedPage) { return { - pathname: `~gitbook/embed/page/${encodeURIComponent(embedPage[1])}`, + pathname: `~gitbook/embed/page/${encodeURIComponent(embedPage[1]!)}`, }; } diff --git a/packages/gitbook/src/routes/ogimage.tsx b/packages/gitbook/src/routes/ogimage.tsx index 546deae10..44cf934ba 100644 --- a/packages/gitbook/src/routes/ogimage.tsx +++ b/packages/gitbook/src/routes/ogimage.tsx @@ -123,7 +123,7 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page body: colorContrast( customization.header.backgroundColor?.[theme] || colors.background, [baseColors.light, baseColors.dark] - ), + )!, }; gridAsset = colors.body === baseColors.light ? gridWhite : gridBlack; break; @@ -134,15 +134,15 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page gradient: colorContrast(customization.styling.primaryColor[theme], [ baseColors.light, baseColors.dark, - ]), + ])!, title: colorContrast(customization.styling.primaryColor[theme], [ baseColors.light, baseColors.dark, - ]), + ])!, body: colorContrast(customization.styling.primaryColor[theme], [ baseColors.light, baseColors.dark, - ]), + ])!, }; gridAsset = colors.body === baseColors.light ? gridWhite : gridBlack; break; diff --git a/packages/gitbook/tailwind.config.ts b/packages/gitbook/tailwind.config.ts index cb2a438f6..7112c958a 100644 --- a/packages/gitbook/tailwind.config.ts +++ b/packages/gitbook/tailwind.config.ts @@ -36,14 +36,13 @@ function generateVarShades(varName: string, filter: ColorCategory[] = []) { */ function generateShades(color: string) { const rawShades = shadesOfColor(color); - const shadeMap = shades.reduce( - (acc, shade) => { - acc[shade] = `rgb(${hexToRgb(rawShades[`${shade}`])} / )`; - return acc; - }, - {} as Record - ); + const shadeMap = shades.reduce((acc: Record, shade) => { + // @ts-expect-error + acc[shade] = `rgb(${hexToRgb(rawShades[`${shade}`])} / )`; + return acc; + }, {}); + // @ts-expect-error shadeMap.DEFAULT = shadeMap[500]; return shadeMap; diff --git a/packages/gitbook/tsconfig.json b/packages/gitbook/tsconfig.json index c5afb802e..07c8ec5e6 100644 --- a/packages/gitbook/tsconfig.json +++ b/packages/gitbook/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": true, "incremental": true, "module": "esnext", diff --git a/packages/icons/tsconfig.json b/packages/icons/tsconfig.json index 7c6e25597..17d609250 100644 --- a/packages/icons/tsconfig.json +++ b/packages/icons/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "target": "es2022", "strict": true, + "noUncheckedIndexedAccess": true, "esModuleInterop": true, "skipLibCheck": true, "moduleResolution": "bundler", diff --git a/packages/react-contentkit/tsconfig.json b/packages/react-contentkit/tsconfig.json index 07ae706aa..3576a5729 100644 --- a/packages/react-contentkit/tsconfig.json +++ b/packages/react-contentkit/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": false, "declaration": true, "outDir": "dist", diff --git a/packages/react-math/tsconfig.json b/packages/react-math/tsconfig.json index 07ae706aa..3576a5729 100644 --- a/packages/react-math/tsconfig.json +++ b/packages/react-math/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": false, "declaration": true, "outDir": "dist",