diff --git a/docusaurus.config.js b/docusaurus.config.ts old mode 100755 new mode 100644 similarity index 92% rename from docusaurus.config.js rename to docusaurus.config.ts index 0175bcf4196..4e5e38af443 --- a/docusaurus.config.js +++ b/docusaurus.config.ts @@ -1,9 +1,14 @@ +import type * as Preset from '@docusaurus/preset-classic'; +import type { Config } from '@docusaurus/types'; + import rehypeCodeblockMeta from './src/plugins/rehype-codeblock-meta.mjs'; import rehypeStaticToDynamic from './src/plugins/rehype-static-to-dynamic.mjs'; import rehypeVideoAspectRatio from './src/plugins/rehype-video-aspect-ratio.mjs'; import remarkNpm2Yarn from './src/plugins/remark-npm2yarn.mjs'; +import darkTheme from './src/themes/react-navigation-dark'; +import lightTheme from './src/themes/react-navigation-light'; -export default { +const config: Config = { title: 'React Navigation', tagline: 'Routing and navigation for your React Native apps', url: 'https://reactnavigation.org/', @@ -26,8 +31,8 @@ export default { respectPrefersColorScheme: true, }, prism: { - theme: require('./src/themes/react-navigation-light.js'), - darkTheme: require('./src/themes/react-navigation-dark.js'), + theme: lightTheme, + darkTheme: darkTheme, additionalLanguages: [ 'bash', 'json', @@ -53,7 +58,6 @@ export default { appId: 'QCWXRU195A', apiKey: 'bad995329370d9a9ba50cc4b840a3884', indexName: 'react-navigation', - algoliaOptions: {}, }, navbar: { title: 'React Navigation', @@ -122,7 +126,7 @@ export default { }, ], }, - }, + } satisfies Preset.ThemeConfig, plugins: [ './src/plugins/disable-fully-specified.mjs', './src/plugins/react-navigation-versions.mjs', @@ -179,12 +183,12 @@ export default { remarkPlugins: [[remarkNpm2Yarn, { sync: true }]], }, theme: { - customCss: require.resolve('./src/css/custom.css'), + customCss: './src/css/custom.css', }, googleAnalytics: { trackingID: 'UA-10128745-16', }, - }, + } satisfies Preset.Options, ], ], headTags: [ @@ -217,3 +221,5 @@ export default { '/js/video-playback.js', ], }; + +export default config; diff --git a/package.json b/package.json index c990c144fdc..07471bc96e3 100755 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "clear": "docusaurus clear", "serve": "docusaurus serve", "swizzle": "docusaurus swizzle", + "typecheck": "tsc", "deploy": "DEPLOYMENT_BRANCH=gh-pages docusaurus deploy", "test": "node --test --test-reporter=@voxpelli/node-test-pretty-reporter", "crowdin-upload": "crowdin upload sources --auto-update -b main", @@ -34,13 +35,17 @@ }, "devDependencies": { "@babel/types": "^7.28.5", + "@docusaurus/tsconfig": "^3.9.2", "@ffprobe-installer/ffprobe": "^2.1.2", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", "@voxpelli/node-test-pretty-reporter": "^1.1.2", "markdownlint": "^0.40.0", "markdownlint-cli2": "^0.19.1", "postcss-nesting": "^13.0.2", "prettier": "^3.7.4", - "recast": "^0.23.11" + "recast": "^0.23.11", + "typescript": "^5.9.3" }, "packageManager": "yarn@4.0.2", "browserslist": { diff --git a/src/components/LinkingTester.js b/src/components/LinkingTester.tsx similarity index 83% rename from src/components/LinkingTester.js rename to src/components/LinkingTester.tsx index c826cbf851d..61f09086042 100644 --- a/src/components/LinkingTester.js +++ b/src/components/LinkingTester.tsx @@ -5,17 +5,23 @@ import * as React from 'react'; import Editor from 'react-simple-code-editor'; import RouteMap from './RouteMap'; -const parse = (value) => eval(`(function() { return ${value}; }())`); +const parse = (value: string) => eval(`(function() { return ${value}; }())`); -function Code({ code, theme, language }) { +type Props = { + code: string; + theme: typeof themes.dracula; + language: string; +}; + +function Code({ code, theme, language }: Props) { return ( {({ className, style, tokens, getLineProps, getTokenProps }) => (
           {tokens.map((line, i) => (
-            
+
{line.map((token, key) => ( - + ))}
))} @@ -53,13 +59,15 @@ export default function LinkingTester() { const [path, setPath] = React.useState('/user/@vergil/edit'); const [config, setConfig] = React.useState(() => parse(rawConfig)); - const [pane, setPane] = React.useState('chart'); + const [pane, setPane] = React.useState<'chart' | 'state' | 'action'>('chart'); let state, action; try { state = getStateFromPath(path.replace(/(^\w+:|^)\/\//, ''), config); - action = getActionFromState(state, config); + if (state) { + action = getActionFromState(state, config); + } } catch (e) { // Ignore } @@ -89,15 +97,17 @@ export default function LinkingTester() { }} highlight={(code) => ( - {({ tokens, getLineProps, getTokenProps }) => - tokens.map((line, i) => ( -
- {line.map((token, key) => ( - - ))} -
- )) - } + {({ tokens, getLineProps, getTokenProps }) => ( + <> + {tokens.map((line, i) => ( +
+ {line.map((token, key) => ( + + ))} +
+ ))} + + )}
)} padding={16} @@ -154,7 +164,7 @@ export default function LinkingTester() { ); } -const styles = { +const styles: Record = { code: { display: 'block', fontFamily: 'var(--ifm-font-family-monospace)', diff --git a/src/components/Pre.js b/src/components/Pre.tsx similarity index 78% rename from src/components/Pre.js rename to src/components/Pre.tsx index 978e2655be1..60c1dfc3ffd 100644 --- a/src/components/Pre.js +++ b/src/components/Pre.tsx @@ -12,6 +12,14 @@ const COMMENTS = { FOCUS_END: '// codeblock-focus-end', }; +type Props = { + children: React.ReactElement<{ className?: string; children: string }>; + 'data-name'?: string; + 'data-snack'?: string | 'embed'; + 'data-dependencies'?: string; + 'data-lang'?: string; +}; + export default function Pre({ children, 'data-name': name, @@ -19,12 +27,17 @@ export default function Pre({ 'data-dependencies': deps, 'data-lang': lang, ...rest -}) { +}: Props) { const [isExpanded, setIsExpanded] = React.useState(false); const { colorMode } = useColorMode(); - const activeVersion = useActiveVersion(); - const { versions } = usePluginData('react-navigation-versions'); + const activeVersion = useActiveVersion(undefined); + const { versions } = usePluginData('react-navigation-versions') as { + versions: Record< + string, + Record]> + >; + }; const child = React.Children.only(children); @@ -57,15 +70,17 @@ export default function Pre({ }) .join('\n'); - children = React.cloneElement(child, { - className: `language-${lang}`, - children: content, - }); - - return {children}; + return ( + + {React.cloneElement(child, { + className: `language-${lang}`, + children: content, + })} + + ); } - const buttons = []; + const buttons: React.ReactNode[] = []; if (child.props.children.includes?.(COMMENTS.FOCUS_START)) { buttons.push( @@ -106,7 +121,7 @@ export default function Pre({ const version = activeVersion?.name; // Handle snack demos - if (snack && versions[version] != null) { + if (snack && version && versions[version] != null) { const code = child.props.children; if (typeof code !== 'string') { @@ -115,7 +130,7 @@ export default function Pre({ ); } - const dependencies = deps + const dependencies: Record = deps ? Object.fromEntries( deps.split(',').map((entry) => { let prefix = ''; @@ -135,34 +150,37 @@ export default function Pre({ Object.assign( dependencies, - Object.entries(versions[version]).reduce((acc, [key, value]) => { - if (code.includes(`from '${key}'`)) { - if (Array.isArray(value)) { - const [v, peers] = value; - - Object.assign(acc, { - [key]: v, - ...Object.fromEntries( - Object.entries(peers).map(([key, value]) => { - const meta = versions[version][key]; - - if (value === '*' && meta) { - const v = Array.isArray(meta) ? meta[0] : meta; - - return [key, v]; - } - - return [key, value]; - }) - ), - }); - } else { - acc[key] = value; + Object.entries(versions[version]).reduce( + (acc, [key, value]) => { + if (code.includes(`from '${key}'`)) { + if (Array.isArray(value)) { + const [v, peers] = value; + + Object.assign(acc, { + [key]: v, + ...Object.fromEntries( + Object.entries(peers).map(([key, value]) => { + const meta = versions[version][key]; + + if (value === '*' && meta) { + const v = Array.isArray(meta) ? meta[0] : meta; + + return [key, v]; + } + + return [key, value]; + }) + ), + }); + } else { + acc[key] = value; + } } - } - return acc; - }, {}) + return acc; + }, + {} as Record + ) ); const url = new URL('https://snack.expo.dev'); @@ -209,7 +227,6 @@ export default function Pre({ style={{ width: '100%', height: 660, - border: 'none', border: '1px solid var(--ifm-table-border-color)', borderRadius: 'var(--ifm-global-radius)', overflow: 'hidden', @@ -263,7 +280,14 @@ export default function Pre({ ); } -function FocusedCodeBlock({ children, expanded, ...rest }) { +function FocusedCodeBlock({ + children, + expanded, + ...rest +}: { + children: React.ReactElement<{ children: string }>; + expanded: boolean; +}) { const child = React.Children.only(children); const code = child.props.children; @@ -274,7 +298,7 @@ function FocusedCodeBlock({ children, expanded, ...rest }) { if (expanded) { content = code .split('\n') - .filter((line) => + .filter((line: string) => [COMMENTS.FOCUS_START, COMMENTS.FOCUS_END].every( (comment) => line.trim() !== comment ) @@ -284,7 +308,7 @@ function FocusedCodeBlock({ children, expanded, ...rest }) { const lines = code.split('\n'); let focus = false; - let indent; + let indent: string | undefined; for (const line of lines) { if (line.trim() === COMMENTS.FOCUS_START) { @@ -293,10 +317,10 @@ function FocusedCodeBlock({ children, expanded, ...rest }) { focus = false; } else if (focus) { if (indent === undefined) { - indent = line.match(/^\s*/)[0]; + indent = line.match(/^\s*/)?.[0]; } - if (line.startsWith(indent)) { + if (indent && line.startsWith(indent)) { content += line.slice(indent.length) + '\n'; } else { content += line + '\n'; @@ -305,8 +329,12 @@ function FocusedCodeBlock({ children, expanded, ...rest }) { } } - children = React.Children.map(children, (c) => - React.cloneElement(c, { children: content }) + return ( + + {React.Children.map(children, (c) => + React.cloneElement(c, { children: content }) + )} + ); } diff --git a/src/components/RouteMap.js b/src/components/RouteMap.tsx similarity index 91% rename from src/components/RouteMap.js rename to src/components/RouteMap.tsx index a8401abab73..a8cb65139b2 100644 --- a/src/components/RouteMap.js +++ b/src/components/RouteMap.tsx @@ -4,7 +4,20 @@ const indigo = '#3F51B5'; const teal = '#009688'; const pink = '#E91E63'; -export default function RouteMap({ routes, root = true }) { +type Route = { + name: string; + params?: Record; + state?: { + routes: Route[]; + }; +}; + +type Props = { + routes: Route[]; + root?: boolean; +}; + +export default function RouteMap({ routes, root = true }: Props) { return (
= { container: { display: 'flex', flexDirection: 'column', diff --git a/src/pages/home/Features/index.js b/src/pages/home/Features/index.js deleted file mode 100644 index 562bdcbf340..00000000000 --- a/src/pages/home/Features/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; - -import styles from './styles.module.css'; - -export default function Features() { - const features = [ - { - image: '/img/home_smile.svg', - title: `Easy to Use`, - description: ` - Start quickly with built-in navigators that deliver a seamless - out-of-the-box experience. - `, - }, - { - image: '/img/home_devices.svg', - title: `Components built for iOS and Android`, - description: ` - Platform-specific look-and-feel with smooth animations and gestures. - `, - }, - { - image: '/img/home_star.svg', - title: `Completely customizable`, - description: ` - If you know how to write apps using JavaScript you can customize any - part of React Navigation. - `, - }, - { - image: '/img/home_extend.svg', - title: `Extensible platform`, - description: ` - React Navigation is extensible at every layer— you can write your own - navigators or even replace the user-facing API. - `, - }, - ]; - - return ( -
-
- {features.map((feature) => ( -
- -
{feature.title}
-

{feature.description}

-
- ))} -
-
- ); -} diff --git a/src/pages/index.js b/src/pages/home/Features/index.tsx old mode 100755 new mode 100644 similarity index 53% rename from src/pages/index.js rename to src/pages/home/Features/index.tsx index 9ad7899c428..c7073a4f77a --- a/src/pages/index.js +++ b/src/pages/home/Features/index.tsx @@ -1,14 +1,8 @@ -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Layout from '@theme/Layout'; -import React from 'react'; - -import Features from './home/Features'; -import Footer from './home/Footer'; -import Splash from './home/Splash'; -import Sponsors from './home/Sponsors'; +import styles from './styles.module.css'; const features = [ { + image: '/img/home_smile.svg', title: `Easy to Use`, description: ` Start quickly with built-in navigators that deliver a seamless @@ -16,12 +10,14 @@ const features = [ `, }, { + image: '/img/home_devices.svg', title: `Components built for iOS and Android`, description: ` Platform-specific look-and-feel with smooth animations and gestures. `, }, { + image: '/img/home_star.svg', title: `Completely customizable`, description: ` If you know how to write apps using JavaScript you can customize any @@ -29,6 +25,7 @@ const features = [ `, }, { + image: '/img/home_extend.svg', title: `Extensible platform`, description: ` React Navigation is extensible at every layer— you can write your own @@ -37,22 +34,18 @@ const features = [ }, ]; -function Home() { - const context = useDocusaurusContext(); - const { siteConfig = {} } = context; - +export default function Features() { return ( - - - - -