diff --git a/.eslintignore b/.eslintignore index 6e940531fa67..b9cb4992d8e7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -22,6 +22,7 @@ packages/docusaurus-plugin-debug/lib/ packages/docusaurus-plugin-sitemap/lib/ packages/docusaurus-plugin-ideal-image/lib/ packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js +packages/docusaurus-theme-classic/lib/ packages/docusaurus-1.x/.eslintrc.js packages/docusaurus-init/templates/facebook/.eslintrc.js diff --git a/.gitignore b/.gitignore index 5fe68e835d72..1dba5a650855 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ packages/docusaurus-plugin-content-pages/lib/ packages/docusaurus-plugin-debug/lib/ packages/docusaurus-plugin-sitemap/lib/ packages/docusaurus-plugin-ideal-image/lib/ +packages/docusaurus-theme-classic/lib/ diff --git a/package.json b/package.json index f02cff5be3c5..7f694d8c5680 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@babel/core": "^7.9.0", + "@babel/cli": "^7.9.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-proposal-optional-chaining": "^7.9.0", "@babel/preset-typescript": "^7.9.0", @@ -52,6 +53,7 @@ "@types/lodash.pick": "^4.4.6", "@types/lodash.pickby": "^4.6.6", "@types/node": "^13.11.0", + "@types/prismjs": "^1.16.1", "@types/react": "^16.9.38", "@types/react-dev-utils": "^9.0.1", "@types/react-helmet": "^6.0.0", diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 0ce9464b708a..ae41e1d54c3e 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -37,10 +37,9 @@ declare module '@generated/routesChunkNames' { export default routesChunkNames; } -declare module '@theme/*' { - const component: any; - export default component; -} +declare module '@theme/*'; + +declare module '@theme-original/*'; declare module '@docusaurus/*'; diff --git a/packages/docusaurus-theme-classic/babel.config.js b/packages/docusaurus-theme-classic/babel.config.js new file mode 100644 index 000000000000..a6b570f38777 --- /dev/null +++ b/packages/docusaurus-theme-classic/babel.config.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: [['@babel/preset-typescript', {isTSX: true, allExtensions: true}]], +}; diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index adc547a420bd..f544452cf025 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -7,6 +7,11 @@ "access": "public" }, "license": "MIT", + "scripts": { + "tsc": "tsc --noEmit && yarn babel && yarn prettier", + "babel": "babel src -d lib --extensions \".tsx,.ts\" --copy-files", + "prettier": "prettier --config ../../.prettierrc --write \"**/*.{js,ts}\"" + }, "dependencies": { "@mdx-js/mdx": "^1.5.8", "@mdx-js/react": "^1.5.8", @@ -20,6 +25,9 @@ "react-router-dom": "^5.1.2", "react-toggle": "^4.1.1" }, + "devDependencies": { + "@docusaurus/module-type-aliases": "^2.0.0-alpha.58" + }, "peerDependencies": { "@docusaurus/core": "^2.0.0", "react": "^16.8.4", diff --git a/packages/docusaurus-theme-classic/src/index.js b/packages/docusaurus-theme-classic/src/index.js index cbe3e4c91410..58bb3ee111e3 100644 --- a/packages/docusaurus-theme-classic/src/index.js +++ b/packages/docusaurus-theme-classic/src/index.js @@ -60,6 +60,10 @@ module.exports = function (context, options) { name: 'docusaurus-theme-classic', getThemePath() { + return path.join(__dirname, '..', 'lib', 'theme'); + }, + + getTypeScriptThemePath() { return path.resolve(__dirname, './theme'); }, diff --git a/packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.js b/packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.tsx similarity index 92% rename from packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.js rename to packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.tsx index 031d9cc01ba4..761123214871 100644 --- a/packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.js +++ b/packages/docusaurus-theme-classic/src/theme/AnnouncementBar/index.tsx @@ -11,9 +11,9 @@ import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext'; import styles from './styles.module.css'; -function AnnouncementBar() { +function AnnouncementBar(): JSX.Element | null { const { - siteConfig: {themeConfig: {announcementBar = {}}} = {}, + siteConfig: {themeConfig: {announcementBar = {}} = {}} = {}, } = useDocusaurusContext(); const {content, backgroundColor, textColor} = announcementBar; const { diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.js b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx similarity index 90% rename from packages/docusaurus-theme-classic/src/theme/BlogListPage/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx index 6426bc8874bf..81de70722e1a 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx @@ -12,7 +12,12 @@ import Layout from '@theme/Layout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogListPaginator from '@theme/BlogListPaginator'; -function BlogListPage(props) { +type Props = { + metadata: {permalink: string; title: string}; + items: {content}[]; +}; + +function BlogListPage(props: Props): JSX.Element { const {metadata, items} = props; const { siteConfig: {title: siteTitle}, diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.js b/packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.tsx similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.tsx index a14ca3cf6a7b..8593728803a8 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPaginator/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import Link from '@docusaurus/Link'; -function BlogListPaginator(props) { +function BlogListPaginator(props): JSX.Element { const {metadata} = props; const {previousPage, nextPage} = metadata; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.js b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx index 892dac2a713d..9f2ad1e01547 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx @@ -31,7 +31,7 @@ const MONTHS = [ 'December', ]; -function BlogPostItem(props) { +function BlogPostItem(props): JSX.Element { const { children, frontMatter, diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index 7881bba0850b..c17a41d809b3 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -11,7 +11,7 @@ import Layout from '@theme/Layout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogPostPaginator from '@theme/BlogPostPaginator'; -function BlogPostPage(props) { +function BlogPostPage(props): JSX.Element { const {content: BlogPostContents} = props; const {frontMatter, metadata} = BlogPostContents; const {title, description, nextItem, prevItem, editUrl} = metadata; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.js b/packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.tsx similarity index 96% rename from packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.tsx index 6dacef1c8ea9..1028cc588abb 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPaginator/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import Link from '@docusaurus/Link'; -function BlogPostPaginator(props) { +function BlogPostPaginator(props): JSX.Element { const {nextItem, prevItem} = props; return ( diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.js b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx similarity index 86% rename from packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx index ba490ac9da7f..ace1226d74b8 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx @@ -10,15 +10,17 @@ import React from 'react'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; -function getCategoryOfTag(tag) { +function getCategoryOfTag(tag: string) { // tag's category should be customizable return tag[0].toUpperCase(); } -function BlogTagsListPage(props) { +type Tag = {permalink: string; name: string; count: number}; + +function BlogTagsListPage(props: {tags: Record}): JSX.Element { const {tags} = props; - const tagCategories = {}; + const tagCategories: {[category: string]: string[]} = {}; Object.keys(tags).forEach((tag) => { const category = getCategoryOfTag(tag); tagCategories[category] = tagCategories[category] || []; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.js b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx similarity index 93% rename from packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.js rename to packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index 41d10517f8e7..e3da4fb4b399 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -11,11 +11,11 @@ import Layout from '@theme/Layout'; import BlogPostItem from '@theme/BlogPostItem'; import Link from '@docusaurus/Link'; -function pluralize(count, word) { +function pluralize(count: number, word: string) { return count > 1 ? `${word}s` : word; } -function BlogTagsPostPage(props) { +function BlogTagsPostPage(props): JSX.Element { const {metadata, items} = props; const {allTagsPath, name: tagName, count} = metadata; diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx similarity index 90% rename from packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js rename to packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 38b5e12615d4..df897c5522d6 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -87,7 +87,15 @@ const highlightDirectiveRegex = (lang) => { }; const codeBlockTitleRegex = /title=".*"/; -export default ({children, className: languageClassName, metastring}) => { +export default ({ + children, + className: languageClassName, + metastring, +}: { + children: string; + className: string; + metastring: string; +}): JSX.Element => { const { siteConfig: { themeConfig: {prism = {}}, @@ -108,21 +116,25 @@ export default ({children, className: languageClassName, metastring}) => { }, []); const button = useRef(null); - let highlightLines = []; + let highlightLines: number[] = []; let codeBlockTitle = ''; const prismTheme = usePrismTheme(); if (metastring && highlightLinesRangeRegex.test(metastring)) { - const highlightLinesRange = metastring.match(highlightLinesRangeRegex)[1]; + // Tested above + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const highlightLinesRange = metastring.match(highlightLinesRangeRegex)![1]; highlightLines = rangeParser .parse(highlightLinesRange) .filter((n) => n > 0); } if (metastring && codeBlockTitleRegex.test(metastring)) { + // Tested above + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion codeBlockTitle = metastring - .match(codeBlockTitleRegex)[0] + .match(codeBlockTitleRegex)![0] .split('title=')[1] .replace(/"+/g, ''); } @@ -151,7 +163,10 @@ export default ({children, className: languageClassName, metastring}) => { if (match !== null) { const directive = match .slice(1) - .reduce((final, item) => final || item, undefined); + .reduce( + (final: string | undefined, item) => final || item, + undefined, + ); switch (directive) { case 'highlight-next-line': range += `${lineNumber},`; @@ -188,9 +203,10 @@ export default ({children, className: languageClassName, metastring}) => { return ( {({className, style, tokens, getLineProps, getTokenProps}) => ( <> @@ -211,7 +227,7 @@ export default ({children, className: languageClassName, metastring}) => { {showCopied ? 'Copied' : 'Copy'}
diff --git a/packages/docusaurus-theme-classic/src/theme/DocItem/index.js b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/DocItem/index.js rename to packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx index 95259336fa06..93e996c9def7 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocItem/index.js +++ b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx @@ -33,7 +33,7 @@ function DocTOC({headings}) { } /* eslint-disable jsx-a11y/control-has-associated-label */ -function Headings({headings, isChild}) { +function Headings({headings, isChild}: {headings; isChild?: boolean}) { if (!headings.length) { return null; } @@ -58,7 +58,7 @@ function Headings({headings, isChild}) { ); } -function DocItem(props) { +function DocItem(props): JSX.Element { const {siteConfig = {}} = useDocusaurusContext(); const {url: siteUrl, title: siteTitle} = siteConfig; const {content: DocContent} = props; diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.js b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/DocPage/index.js rename to packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx index adee2291b84c..95c74c2b4bec 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.js +++ b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx @@ -18,7 +18,7 @@ import {matchPath} from '@docusaurus/router'; import styles from './styles.module.css'; -function DocPage(props) { +function DocPage(props): JSX.Element { const {route: baseRoute, docsMetadata, location} = props; // case-sensitive route such as it is defined in the sidebar const currentRoute = diff --git a/packages/docusaurus-theme-classic/src/theme/DocPaginator/index.js b/packages/docusaurus-theme-classic/src/theme/DocPaginator/index.tsx similarity index 87% rename from packages/docusaurus-theme-classic/src/theme/DocPaginator/index.js rename to packages/docusaurus-theme-classic/src/theme/DocPaginator/index.tsx index b771f38175d6..3d0f917f8f56 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPaginator/index.js +++ b/packages/docusaurus-theme-classic/src/theme/DocPaginator/index.tsx @@ -8,7 +8,13 @@ import React from 'react'; import Link from '@docusaurus/Link'; -function DocPaginator(props) { +type PageInfo = {permalink: string; title: string}; + +type Props = { + metadata: {previous: PageInfo; next: PageInfo}; +}; + +function DocPaginator(props: Props): JSX.Element { const {metadata} = props; return ( diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js rename to packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx index 38504c8ab32d..558658649363 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx @@ -163,11 +163,11 @@ function DocSidebarItem(props) { } } -function DocSidebar(props) { +function DocSidebar(props): JSX.Element | null { const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false); const { siteConfig: { - themeConfig: {navbar: {title, hideOnScroll = false} = {}}, + themeConfig: {navbar: {title = '', hideOnScroll = false} = {}} = {}, } = {}, isClient, } = useDocusaurusContext(); diff --git a/packages/docusaurus-theme-classic/src/theme/Footer/index.js b/packages/docusaurus-theme-classic/src/theme/Footer/index.tsx similarity index 98% rename from packages/docusaurus-theme-classic/src/theme/Footer/index.js rename to packages/docusaurus-theme-classic/src/theme/Footer/index.tsx index a60e0c707246..a942520f1b9f 100644 --- a/packages/docusaurus-theme-classic/src/theme/Footer/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Footer/index.tsx @@ -39,7 +39,7 @@ const FooterLogo = ({url, alt}) => ( {alt} ); -function Footer() { +function Footer(): JSX.Element | null { const context = useDocusaurusContext(); const {siteConfig = {}} = context; const {themeConfig = {}} = siteConfig; diff --git a/packages/docusaurus-theme-classic/src/theme/Heading/index.js b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx similarity index 87% rename from packages/docusaurus-theme-classic/src/theme/Heading/index.js rename to packages/docusaurus-theme-classic/src/theme/Heading/index.tsx index e649f680bd77..1e114dfd9017 100644 --- a/packages/docusaurus-theme-classic/src/theme/Heading/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx @@ -7,14 +7,14 @@ /* eslint-disable jsx-a11y/anchor-has-content, jsx-a11y/anchor-is-valid */ -import React from 'react'; +import React, {ComponentType} from 'react'; import clsx from 'clsx'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import './styles.css'; import styles from './styles.module.css'; -const Heading = (Tag) => +const Heading = (Tag: ComponentType): ((props) => JSX.Element) => function TargetComponent({id, ...props}) { const { siteConfig: { @@ -30,7 +30,7 @@ const Heading = (Tag) => ; } return ; }, - pre: (props) =>
, + pre: (props: ComponentProps<'div'>): JSX.Element => ( +
+ ), h1: Heading('h1'), h2: Heading('h2'), h3: Heading('h3'), diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js b/packages/docusaurus-theme-classic/src/theme/Navbar/index.tsx similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/Navbar/index.js rename to packages/docusaurus-theme-classic/src/theme/Navbar/index.tsx index 0bb5b70c6462..798a4139f08a 100644 --- a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/index.tsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useCallback, useState, useEffect} from 'react'; +import React, {useCallback, useState, useEffect, ComponentProps} from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; @@ -33,7 +33,15 @@ function NavLink({ activeClassName = 'navbar__link--active', prependBaseUrlToHref, ...props -}) { +}: { + activeBasePath?: string; + activeBaseRegex?: string; + to?: string; + href?: string; + label?: string; + activeClassName?: string; + prependBaseUrlToHref?: string; +} & ComponentProps<'a'>) { const toUrl = useBaseUrl(to); const activeBaseUrl = useBaseUrl(activeBasePath); const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true}); @@ -96,7 +104,8 @@ function NavItem({ onClick={(e) => e.preventDefault()} onKeyDown={(e) => { if (e.key === 'Enter') { - e.target.parentNode.classList.toggle('dropdown--show'); + ((e.target as HTMLElement) + .parentNode as HTMLElement).classList.toggle('dropdown--show'); } }}> {props.label} @@ -171,11 +180,11 @@ function splitLinks(links) { }; } -function Navbar() { +function Navbar(): JSX.Element { const { siteConfig: { themeConfig: { - navbar: {title, links = [], hideOnScroll = false} = {}, + navbar: {title = '', links = [], hideOnScroll = false} = {}, disableDarkMode = false, }, }, diff --git a/packages/docusaurus-theme-classic/src/theme/NotFound.js b/packages/docusaurus-theme-classic/src/theme/NotFound.tsx similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/NotFound.js rename to packages/docusaurus-theme-classic/src/theme/NotFound.tsx index 955f51e2b4f7..c06fd3e53d60 100644 --- a/packages/docusaurus-theme-classic/src/theme/NotFound.js +++ b/packages/docusaurus-theme-classic/src/theme/NotFound.tsx @@ -8,7 +8,7 @@ import React from 'react'; import Layout from '@theme/Layout'; -function NotFound() { +function NotFound(): JSX.Element { return (
diff --git a/packages/docusaurus-theme-classic/src/theme/SearchBar.js b/packages/docusaurus-theme-classic/src/theme/SearchBar.tsx similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/SearchBar.js rename to packages/docusaurus-theme-classic/src/theme/SearchBar.tsx diff --git a/packages/docusaurus-theme-classic/src/theme/TabItem/index.js b/packages/docusaurus-theme-classic/src/theme/TabItem/index.tsx similarity index 69% rename from packages/docusaurus-theme-classic/src/theme/TabItem/index.js rename to packages/docusaurus-theme-classic/src/theme/TabItem/index.tsx index 709748b85c4f..6b1309d864b9 100644 --- a/packages/docusaurus-theme-classic/src/theme/TabItem/index.js +++ b/packages/docusaurus-theme-classic/src/theme/TabItem/index.tsx @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {ReactNode} from 'react'; -function TabItem(props) { +function TabItem(props: {readonly children: ReactNode}): JSX.Element { return
{props.children}
; } diff --git a/packages/docusaurus-theme-classic/src/theme/Tabs/index.js b/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx similarity index 85% rename from packages/docusaurus-theme-classic/src/theme/Tabs/index.js rename to packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx index 71d03ab6e082..f714a15ff1ef 100644 --- a/packages/docusaurus-theme-classic/src/theme/Tabs/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useState, Children} from 'react'; +import React, {useState, Children, ReactElement} from 'react'; import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext'; import clsx from 'clsx'; @@ -17,7 +17,15 @@ const keys = { right: 39, }; -function Tabs(props) { +type Props = { + block?: boolean; + children: ReactElement<{value: string}>[]; + defaultValue?: string; + values: {value: string; label: string}[]; + groupId?: string; +}; + +function Tabs(props: Props): JSX.Element { const {block, children, defaultValue, values, groupId} = props; const {tabGroupChoices, setTabGroupChoices} = useUserPreferencesContext(); const [selectedValue, setSelectedValue] = useState(defaultValue); @@ -40,7 +48,7 @@ function Tabs(props) { } }; - const tabRefs = []; + const tabRefs: (HTMLLIElement | null)[] = []; const focusNextTab = (tabs, target) => { const next = tabs.indexOf(target) + 1; @@ -86,7 +94,7 @@ function Tabs(props) { {values.map(({value, label}) => (
  • { Children.toArray(children).filter( - (child) => child.props.value === selectedValue, + (child) => + (child as ReactElement<{value: string}>).props.value === + selectedValue, )[0] }
  • diff --git a/packages/docusaurus-theme-classic/src/theme/ThemeContext.js b/packages/docusaurus-theme-classic/src/theme/ThemeContext.ts similarity index 82% rename from packages/docusaurus-theme-classic/src/theme/ThemeContext.js rename to packages/docusaurus-theme-classic/src/theme/ThemeContext.ts index e78020cad7fb..ad265a49e499 100644 --- a/packages/docusaurus-theme-classic/src/theme/ThemeContext.js +++ b/packages/docusaurus-theme-classic/src/theme/ThemeContext.ts @@ -7,6 +7,6 @@ import React from 'react'; -const ThemeContext = React.createContext(); +const ThemeContext = React.createContext(undefined); export default ThemeContext; diff --git a/packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.js b/packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.tsx similarity index 81% rename from packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.js rename to packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.tsx index 10e8de0d872f..be26e0d16551 100644 --- a/packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.js +++ b/packages/docusaurus-theme-classic/src/theme/ThemeProvider/index.tsx @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {ReactNode} from 'react'; import useTheme from '@theme/hooks/useTheme'; import ThemeContext from '@theme/ThemeContext'; -function ThemeProvider(props) { +function ThemeProvider(props: {readonly children: ReactNode}): JSX.Element { const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme(); return ( diff --git a/packages/docusaurus-theme-classic/src/theme/Toggle/index.js b/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx similarity index 85% rename from packages/docusaurus-theme-classic/src/theme/Toggle/index.js rename to packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx index cdb0956abf5f..a73e95e05551 100644 --- a/packages/docusaurus-theme-classic/src/theme/Toggle/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {ComponentProps} from 'react'; import Toggle from 'react-toggle'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; @@ -16,7 +16,7 @@ import styles from './styles.module.css'; const Moon = () => ; const Sun = () => ; -export default function (props) { +export default function (props: ComponentProps): JSX.Element { const {isClient} = useDocusaurusContext(); return ( { +const useAnnouncementBar = (): { + isAnnouncementBarClosed: boolean; + closeAnnouncementBar: () => void; +} => { const { - siteConfig: {themeConfig: {announcementBar: {id} = {}}} = {}, + siteConfig: { + themeConfig: {announcementBar: {id = 'annoucement-bar'} = {}} = {}, + } = {}, } = useDocusaurusContext(); const [isClosed, setClosed] = useState(true); const handleClose = () => { - localStorage.setItem(STORAGE_DISMISS_KEY, true); + localStorage.setItem(STORAGE_DISMISS_KEY, 'true'); setClosed(true); }; @@ -32,7 +37,7 @@ const useAnnouncementBar = () => { localStorage.setItem(STORAGE_ID_KEY, id); if (isNewAnnouncement) { - localStorage.setItem(STORAGE_DISMISS_KEY, false); + localStorage.setItem(STORAGE_DISMISS_KEY, 'false'); } if ( diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts similarity index 97% rename from packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts index d5085dc80882..b954aa1cf25d 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.ts @@ -10,7 +10,7 @@ import {useLocation} from '@docusaurus/router'; import useLocationHash from '@theme/hooks/useLocationHash'; import useScrollPosition from '@theme/hooks/useScrollPosition'; -const useHideableNavbar = (hideOnScroll) => { +const useHideableNavbar = (hideOnScroll: boolean) => { const [isNavbarVisible, setIsNavbarVisible] = useState(true); const [isFocusedAnchor, setIsFocusedAnchor] = useState(false); const [lastScrollTop, setLastScrollTop] = useState(0); diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.js b/packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.ts similarity index 76% rename from packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.ts index 1f3c3061d4b0..0b4d75a342f5 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useLocationHash.ts @@ -5,9 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {useState, useEffect} from 'react'; +import {useState, useEffect, Dispatch, SetStateAction} from 'react'; -function useLocationHash(initialHash) { +function useLocationHash( + initialHash: string, +): [string, Dispatch>] { const [hash, setHash] = useState(initialHash); useEffect(() => { diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.js b/packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.ts similarity index 88% rename from packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.ts index c8ea25c99936..4fe49ecd45d4 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useLockBodyScroll.ts @@ -7,7 +7,7 @@ import {useEffect} from 'react'; -function useLockBodyScroll(lock = true) { +function useLockBodyScroll(lock: boolean = true): void { useEffect(() => { document.body.style.overflow = lock ? 'hidden' : 'visible'; diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useLogo.js b/packages/docusaurus-theme-classic/src/theme/hooks/useLogo.ts similarity index 77% rename from packages/docusaurus-theme-classic/src/theme/hooks/useLogo.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useLogo.ts index 2cbb6fad16b3..4631f8aec329 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useLogo.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useLogo.ts @@ -10,13 +10,20 @@ import useThemeContext from '@theme/hooks/useThemeContext'; import useBaseUrl from '@docusaurus/useBaseUrl'; import isInternalUrl from '@docusaurus/isInternalUrl'; -const useLogo = () => { +type LogoLinkProps = {target?: string; rel?: string}; + +const useLogo = (): { + logoLink: string; + logoLinkProps: LogoLinkProps; + logoImageUrl: string; + logoAlt: string; +} => { const { - siteConfig: {themeConfig: {navbar: {logo = {}} = {}}} = {}, + siteConfig: {themeConfig: {navbar: {logo = {}} = {}} = {}} = {}, } = useDocusaurusContext(); const {isDarkTheme} = useThemeContext(); const logoLink = useBaseUrl(logo.href || '/'); - let logoLinkProps = {}; + let logoLinkProps: LogoLinkProps = {}; if (logo.target) { logoLinkProps = {target: logo.target}; diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.js b/packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.ts similarity index 93% rename from packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.js rename to packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.ts index bc3b28f6f3b9..90ac0a4673f4 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/usePrismTheme.ts @@ -9,7 +9,7 @@ import defaultTheme from 'prism-react-renderer/themes/palenight'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useThemeContext from '@theme/hooks/useThemeContext'; -const usePrismTheme = () => { +const usePrismTheme = (): typeof defaultTheme => { const { siteConfig: { themeConfig: {prism = {}}, diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js b/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.ts similarity index 76% rename from packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.ts index 4295b1d9ff3c..81c352c149e9 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.ts @@ -8,12 +8,17 @@ import {useState, useEffect} from 'react'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; -const getScrollPosition = () => ({ +type ScrollPosition = {scrollX: number; scrollY: number}; + +const getScrollPosition = (): ScrollPosition => ({ scrollX: ExecutionEnvironment.canUseDOM ? window.pageXOffset : 0, scrollY: ExecutionEnvironment.canUseDOM ? window.pageYOffset : 0, }); -const useScrollPosition = (effect, deps = []) => { +const useScrollPosition = ( + effect: (position: ScrollPosition) => void, + deps = [], +): ScrollPosition => { const [scrollPosition, setScrollPosition] = useState(getScrollPosition()); const handleScroll = () => { @@ -31,6 +36,7 @@ const useScrollPosition = (effect, deps = []) => { return () => window.removeEventListener('scroll', handleScroll, { + // @ts-expect-error: See https://github.com/microsoft/TypeScript/issues/32912 passive: true, }); }, deps); diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.js b/packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.ts similarity index 82% rename from packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.ts index 81ddd52e42bf..16cb1fe573d6 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useTOCHighlight.ts @@ -7,17 +7,23 @@ import {useEffect, useState} from 'react'; -function useTOCHighlight(linkClassName, linkActiveClassName, topOffset) { - const [lastActiveLink, setLastActiveLink] = useState(undefined); +function useTOCHighlight( + linkClassName: string, + linkActiveClassName: string, + topOffset: number, +): void { + const [lastActiveLink, setLastActiveLink] = useState( + undefined!, + ); useEffect(() => { - let headersAnchors = []; - let links = []; + let headersAnchors: HTMLCollectionOf; + let links: HTMLCollectionOf; function setActiveLink() { function getActiveHeaderAnchor() { let index = 0; - let activeHeaderAnchor = null; + let activeHeaderAnchor: Element | null = null; headersAnchors = document.getElementsByClassName('anchor'); while (index < headersAnchors.length && !activeHeaderAnchor) { @@ -40,6 +46,7 @@ function useTOCHighlight(linkClassName, linkActiveClassName, topOffset) { let index = 0; let itemHighlighted = false; + // @ts-expect-error: Must be
    tags. links = document.getElementsByClassName(linkClassName); while (index < links.length && !itemHighlighted) { const link = links[index]; diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.js b/packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.ts similarity index 75% rename from packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.ts index 2a2a98e8bfce..3c7bac9f137e 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useTabGroupChoice.ts @@ -9,8 +9,13 @@ import {useState, useCallback, useEffect} from 'react'; const TAB_CHOICE_PREFIX = 'docusaurus.tab.'; -const useTabGroupChoice = () => { - const [tabGroupChoices, setChoices] = useState({}); +const useTabGroupChoice = (): { + tabGroupChoices: {readonly [groupId: string]: string}; + setTabGroupChoices: (groupId: string, newChoice: string) => void; +} => { + const [tabGroupChoices, setChoices] = useState<{ + readonly [groupId: string]: string; + }>({}); const setChoiceSyncWithLocalStorage = useCallback((groupId, newChoice) => { try { localStorage.setItem(`${TAB_CHOICE_PREFIX}${groupId}`, newChoice); @@ -23,7 +28,7 @@ const useTabGroupChoice = () => { try { const localStorageChoices = {}; for (let i = 0; i < localStorage.length; i += 1) { - const storageKey = localStorage.key(i); + const storageKey = localStorage.key(i) as string; if (storageKey.startsWith(TAB_CHOICE_PREFIX)) { const groupId = storageKey.substring(TAB_CHOICE_PREFIX.length); localStorageChoices[groupId] = localStorage.getItem(storageKey); @@ -37,7 +42,7 @@ const useTabGroupChoice = () => { return { tabGroupChoices, - setTabGroupChoices: (groupId, newChoice) => { + setTabGroupChoices: (groupId: string, newChoice: string) => { setChoices((oldChoices) => ({...oldChoices, [groupId]: newChoice})); setChoiceSyncWithLocalStorage(groupId, newChoice); }, diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useTheme.js b/packages/docusaurus-theme-classic/src/theme/hooks/useTheme.ts similarity index 88% rename from packages/docusaurus-theme-classic/src/theme/hooks/useTheme.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useTheme.ts index a0bd4e6d046a..6cd399b3211b 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useTheme.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useTheme.ts @@ -14,9 +14,13 @@ const themes = { dark: 'dark', }; -const useTheme = () => { +const useTheme = (): { + isDarkTheme: boolean; + setLightTheme: () => void; + setDarkTheme: () => void; +} => { const { - siteConfig: {themeConfig: {disableDarkMode}} = {}, + siteConfig: {themeConfig: {disableDarkMode = false} = {}} = {}, } = useDocusaurusContext(); const [theme, setTheme] = useState( typeof document !== 'undefined' @@ -43,6 +47,7 @@ const useTheme = () => { }, []); useEffect(() => { + // @ts-expect-error: safe to set null as attribute document.documentElement.setAttribute('data-theme', theme); }, [theme]); diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.js b/packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.ts similarity index 69% rename from packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.ts index b995277a801b..889005be2d32 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useThemeContext.ts @@ -9,8 +9,14 @@ import {useContext} from 'react'; import ThemeContext from '@theme/ThemeContext'; -function useThemeContext() { - const context = useContext(ThemeContext); +type ThemeContextProps = { + isDarkTheme: boolean; + setLightTheme: () => void; + setDarkTheme: () => void; +}; + +function useThemeContext(): ThemeContextProps { + const context = useContext(ThemeContext); if (context == null) { throw new Error( '`useThemeContext` is used outside of `Layout` Component. See https://v2.docusaurus.io/docs/theme-classic#usethemecontext.', diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.js b/packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.ts similarity index 55% rename from packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.ts index 347829bba9fd..6dd221c078db 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useUserPreferencesContext.ts @@ -9,8 +9,17 @@ import {useContext} from 'react'; import UserPreferencesContext from '@theme/UserPreferencesContext'; -function useUserPreferencesContext() { - const context = useContext(UserPreferencesContext); +type UserPreferencesContextProps = { + tabGroupChoices: {readonly [groupId: string]: string}; + setTabGroupChoices: (groupId: string, newChoice: string) => void; + isAnnouncementBarClosed: boolean; + closeAnnouncementBar: () => void; +}; + +function useUserPreferencesContext(): UserPreferencesContextProps { + const context = useContext( + UserPreferencesContext, + ); if (context == null) { throw new Error( '`useUserPreferencesContext` is used outside of `Layout` Component.', diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.js b/packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.ts similarity index 87% rename from packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.js rename to packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.ts index 255dbfe5422b..7ec2e32818a6 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useWindowSize.ts @@ -12,9 +12,11 @@ const desktopThresholdWidth = 996; const windowSizes = { desktop: 'desktop', mobile: 'mobile', -}; +} as const; -function useWindowSize() { +type WindowSize = keyof typeof windowSizes; + +function useWindowSize(): WindowSize | undefined { const isClient = typeof window !== 'undefined'; function getSize() { @@ -30,7 +32,7 @@ function useWindowSize() { useEffect(() => { if (!isClient) { - return false; + return undefined; } function handleResize() { diff --git a/packages/docusaurus-theme-classic/src/theme/prism-include-languages.js b/packages/docusaurus-theme-classic/src/theme/prism-include-languages.ts similarity index 74% rename from packages/docusaurus-theme-classic/src/theme/prism-include-languages.js rename to packages/docusaurus-theme-classic/src/theme/prism-include-languages.ts index bf0947ea86dd..e1c813139818 100644 --- a/packages/docusaurus-theme-classic/src/theme/prism-include-languages.js +++ b/packages/docusaurus-theme-classic/src/theme/prism-include-languages.ts @@ -7,16 +7,17 @@ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import siteConfig from '@generated/docusaurus.config'; +import type * as PrismNamespace from 'prismjs'; -const prismIncludeLanguages = (Prism) => { +const prismIncludeLanguages = (PrismObject: typeof PrismNamespace): void => { if (ExecutionEnvironment.canUseDOM) { const { themeConfig: {prism: {additionalLanguages = []} = {}}, } = siteConfig; - window.Prism = Prism; + window.Prism = PrismObject; - additionalLanguages.forEach((lang) => { + additionalLanguages.forEach((lang: string) => { require(`prismjs/components/prism-${lang}`); // eslint-disable-line }); diff --git a/packages/docusaurus-theme-classic/src/types.d.ts b/packages/docusaurus-theme-classic/src/types.d.ts new file mode 100644 index 000000000000..684e0064fbf9 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/types.d.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// eslint-disable-next-line spaced-comment +/// diff --git a/packages/docusaurus-theme-classic/tsconfig.json b/packages/docusaurus-theme-classic/tsconfig.json new file mode 100644 index 000000000000..a27160e60158 --- /dev/null +++ b/packages/docusaurus-theme-classic/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "lib": ["DOM"], + "module": "esnext", + "noEmit": true, + "noImplicitAny": false, + "jsx": "react", + "baseUrl": "src" + }, + "include": ["src/"] +} diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index b9d29faad264..b7ec621cfa7d 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -118,6 +118,7 @@ export interface Plugin { utils: ConfigureWebpackUtils, ): Configuration; getThemePath?(): string; + getTypeScriptThemePath?(): string; getPathsToWatch?(): string[]; getClientModules?(): string[]; extendCli?(cli: Command): void; diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 14df6168903a..542b05ba2450 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -60,7 +60,7 @@ export function objectWithKeySorted(obj: {[index: string]: any}) { } const indexRE = /(^|.*\/)index\.(md|js|jsx|ts|tsx)$/i; -const extRE = /\.(md|js|tsx)$/; +const extRE = /\.(md|js|ts|tsx)$/; /** * Convert filepath to url path. diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 97068cfd00ff..6bba9d1e6a9e 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -60,8 +60,17 @@ cli cli .command('swizzle [componentName] [siteDir]') .description('Copy the theme files into website folder for customization.') - .action((themeName, componentName, siteDir = '.') => { - wrapCommand(swizzle)(path.resolve(siteDir), themeName, componentName); + .option( + '--typescript', + 'Copy TypeScript theme files when possible (default: false)', + ) + .action((themeName, componentName, siteDir = '.', {typescript}) => { + wrapCommand(swizzle)( + path.resolve(siteDir), + themeName, + componentName, + typescript, + ); }); cli diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index c57a59f22caa..f133ab547a00 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -18,13 +18,16 @@ export default async function swizzle( siteDir: string, themeName: string, componentName?: string, + typescript?: boolean, ): Promise { const plugin = importFresh(themeName) as ( context: LoadContext, ) => Plugin; const context = loadContext(siteDir); const pluginInstance = plugin(context); - let fromPath = pluginInstance.getThemePath?.(); + let fromPath = typescript + ? pluginInstance.getTypeScriptThemePath?.() + : pluginInstance.getThemePath?.(); if (fromPath) { let toPath = path.resolve(siteDir, THEME_PATH); @@ -32,10 +35,16 @@ export default async function swizzle( fromPath = path.join(fromPath, componentName); toPath = path.join(toPath, componentName); - // Handle single JavaScript file only. - // E.g: if does not exist, we try to swizzle .js instead - if (!fs.existsSync(fromPath) && fs.existsSync(`${fromPath}.js`)) { - [fromPath, toPath] = [`${fromPath}.js`, `${toPath}.js`]; + // Handle single TypeScript/JavaScript file only. + // E.g: if does not exist, we try to swizzle .(ts|tsx|js) instead + if (!fs.existsSync(fromPath)) { + if (fs.existsSync(`${fromPath}.ts`)) { + [fromPath, toPath] = [`${fromPath}.ts`, `${toPath}.ts`]; + } else if (fs.existsSync(`${fromPath}.tsx`)) { + [fromPath, toPath] = [`${fromPath}.tsx`, `${toPath}.tsx`]; + } else if (fs.existsSync(`${fromPath}.js`)) { + [fromPath, toPath] = [`${fromPath}.js`, `${toPath}.js`]; + } } } await fs.copy(fromPath, toPath); @@ -48,5 +57,13 @@ export default async function swizzle( console.log( `\n${chalk.green('Success!')} Copied ${fromMsg} to ${toMsg}.\n`, ); + } else if (typescript) { + console.warn( + chalk.yellow( + `${themeName} does not provide TypeScript theme code via getTypeScriptThemePath().`, + ), + ); + } else { + console.warn(chalk.yellow(`${themeName} does not provide any theme code.`)); } } diff --git a/tsconfig.json b/tsconfig.json index bf338b3df6bf..e0b11fc6d794 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es2017", "module": "commonjs", - "lib": ["es2017","es2019.array"], + "lib": ["es2017","es2019.array", "DOM"], "declaration": true, "declarationMap": true, diff --git a/website/src/theme/NotFound.js b/website/src/theme/NotFound.tsx similarity index 92% rename from website/src/theme/NotFound.js rename to website/src/theme/NotFound.tsx index 3d7c2b5ddda0..2dcfdcb2c57f 100644 --- a/website/src/theme/NotFound.js +++ b/website/src/theme/NotFound.tsx @@ -9,7 +9,7 @@ import React from 'react'; import Layout from '@theme/Layout'; import Feedback from '../pages/feedback'; -function NotFound({location}) { +function NotFound({location}: {location: {pathname: string}}): JSX.Element { if (/^\/feedback/.test(location.pathname)) { return ; } diff --git a/website/src/types.d.ts b/website/src/types.d.ts new file mode 100644 index 000000000000..684e0064fbf9 --- /dev/null +++ b/website/src/types.d.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// eslint-disable-next-line spaced-comment +/// diff --git a/website/tsconfig.json b/website/tsconfig.json new file mode 100644 index 000000000000..0f11fe0d4ae2 --- /dev/null +++ b/website/tsconfig.json @@ -0,0 +1,13 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "../tsconfig.json", + "compilerOptions": { + "lib": ["DOM"], + "module": "esnext", + "noEmit": true, + "noImplicitAny": false, + "jsx": "react", + "baseUrl": "src" + }, + "include": ["src/"] +} diff --git a/yarn.lock b/yarn.lock index 8e1a24ab842d..bd46cdc5f529 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,22 @@ # yarn lockfile v1 +"@babel/cli@^7.9.0": + version "7.10.3" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.3.tgz#4ea83bd997d2a41c78d07263ada3ec466fb3764b" + integrity sha512-lWB3yH5/fWY8pi2Kj5/fA+17guJ9feSBw5DNjTju3/nRi9sXnl1JPh7aKQOSvdNbiDbkzzoGYtsr46M8dGmXDQ== + dependencies: + commander "^4.0.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.1.0" + glob "^7.0.0" + lodash "^4.17.13" + make-dir "^2.1.0" + slash "^2.0.0" + source-map "^0.5.0" + optionalDependencies: + chokidar "^2.1.8" + "@babel/code-frame@7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" @@ -2839,7 +2855,7 @@ version "17.1.2" resolved "https://registry.yarnpkg.com/@types/hapi__joi/-/hapi__joi-17.1.2.tgz#f547d45b5d33677d1807ec217aeee832dc7e6334" integrity sha512-2S6+hBISRQ5Ca6/9zfQi7zPueWMGyZxox6xicqJuW1/aC/6ambLyh+gDqY5fi8JBuHmGKMHldSfEpIXJtTmGKQ== - + "@types/history@*": version "4.7.6" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356" @@ -3051,6 +3067,11 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== +"@types/prismjs@^1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.1.tgz#50b82947207847db6abcbcd14caa89e3b897c259" + integrity sha512-RNgcK3FEc1GpeOkamGDq42EYkb6yZW5OWQwTS56NJIB8WL0QGISQglA7En7NUx9RGP8AC52DOe+squqbAckXlA== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -5807,7 +5828,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -8301,6 +8322,11 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"