mirror of
https://github.com/explosion/spaCy.git
synced 2025-01-12 02:06:31 +03:00
Load components dynamically (decrease initial file size for docs) (#12175)
* Extract `CodeBlock` component into own file * Extract `InlineCode` component into own file * Extract `TypeAnnotation` component into own file * Convert named `export` to `default export` * Remove unused `export` * Simplify `TypeAnnotation` to remove dependency for Prism * Load `Code` component dynamically * Extract `MarkdownToReact` component into own file * WIP Code Dynamic * Load `MarkdownToReact` component dynamically * Extract `htmlToReact` to own file * Load `htmlToReact` component dynamically * Dynamically load `Juniper`
This commit is contained in:
parent
07dfa54669
commit
056b73468c
|
@ -13,7 +13,7 @@ import {
|
|||
LandingBanner,
|
||||
} from '../src/components/landing'
|
||||
import { H2 } from '../src/components/typography'
|
||||
import { InlineCode } from '../src/components/code'
|
||||
import { InlineCode } from '../src/components/inlineCode'
|
||||
import { Ul, Li } from '../src/components/list'
|
||||
import Button from '../src/components/button'
|
||||
import Link from '../src/components/link'
|
||||
|
|
|
@ -14,96 +14,16 @@ import 'prismjs/components/prism-markdown.min.js'
|
|||
import 'prismjs/components/prism-python.min.js'
|
||||
import 'prismjs/components/prism-yaml.min.js'
|
||||
|
||||
import CUSTOM_TYPES from '../../meta/type-annotations.json'
|
||||
import { isString, htmlToReact } from './util'
|
||||
import { isString } from './util'
|
||||
import Link, { OptionalLink } from './link'
|
||||
import GitHubCode from './github'
|
||||
import Juniper from './juniper'
|
||||
import classes from '../styles/code.module.sass'
|
||||
import siteMetadata from '../../meta/site.json'
|
||||
import { binderBranch } from '../../meta/dynamicMeta.mjs'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const WRAP_THRESHOLD = 30
|
||||
const CLI_GROUPS = ['init', 'debug', 'project', 'ray', 'huggingface-hub']
|
||||
|
||||
const CodeBlock = (props) => (
|
||||
<Pre>
|
||||
<Code {...props} />
|
||||
</Pre>
|
||||
)
|
||||
|
||||
export default CodeBlock
|
||||
|
||||
export const Pre = (props) => {
|
||||
return <pre className={classes['pre']}>{props.children}</pre>
|
||||
}
|
||||
|
||||
export const InlineCode = ({ wrap = false, className, children, ...props }) => {
|
||||
const codeClassNames = classNames(classes['inline-code'], className, {
|
||||
[classes['wrap']]: wrap || (isString(children) && children.length >= WRAP_THRESHOLD),
|
||||
})
|
||||
return (
|
||||
<code className={codeClassNames} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
|
||||
InlineCode.propTypes = {
|
||||
wrap: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
function linkType(el, showLink = true) {
|
||||
if (!isString(el) || !el.length) return el
|
||||
const elStr = el.trim()
|
||||
if (!elStr) return el
|
||||
const typeUrl = CUSTOM_TYPES[elStr]
|
||||
const url = typeUrl == true ? DEFAULT_TYPE_URL : typeUrl
|
||||
const ws = el[0] == ' '
|
||||
return url && showLink ? (
|
||||
<Fragment>
|
||||
{ws && ' '}
|
||||
<Link to={url} hideIcon>
|
||||
{elStr}
|
||||
</Link>
|
||||
</Fragment>
|
||||
) : (
|
||||
el
|
||||
)
|
||||
}
|
||||
|
||||
export const TypeAnnotation = ({ lang = 'python', link = true, children }) => {
|
||||
// Hacky, but we're temporarily replacing a dot to prevent it from being split during highlighting
|
||||
const TMP_DOT = '۔'
|
||||
const code = Array.isArray(children) ? children.join('') : children || ''
|
||||
const [rawText, meta] = code.split(/(?= \(.+\)$)/)
|
||||
const rawStr = rawText.replace(/\./g, TMP_DOT)
|
||||
const rawHtml =
|
||||
lang === 'none' || !code ? code : Prism.highlight(rawStr, Prism.languages[lang], lang)
|
||||
const html = rawHtml.replace(new RegExp(TMP_DOT, 'g'), '.').replace(/\n/g, ' ')
|
||||
const result = htmlToReact(html)
|
||||
const elements = Array.isArray(result) ? result : [result]
|
||||
const annotClassNames = classNames(
|
||||
'type-annotation',
|
||||
`language-${lang}`,
|
||||
classes['inline-code'],
|
||||
classes['type-annotation'],
|
||||
{
|
||||
[classes['wrap']]: code.length >= WRAP_THRESHOLD,
|
||||
}
|
||||
)
|
||||
return (
|
||||
<span className={annotClassNames} role="code" aria-label="Type annotation">
|
||||
{elements.map((el, i) => (
|
||||
<Fragment key={i}>{linkType(el, !!link)}</Fragment>
|
||||
))}
|
||||
{meta && <span className={classes['type-annotation-meta']}>{meta}</span>}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const splitLines = (children) => {
|
||||
const listChildrenPerLine = []
|
||||
|
||||
|
@ -288,7 +208,7 @@ const addLineHighlight = (children, highlight) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const CodeHighlighted = ({ children, highlight, lang }) => {
|
||||
const CodeHighlighted = ({ children, highlight, lang }) => {
|
||||
const [html, setHtml] = useState()
|
||||
|
||||
useEffect(
|
||||
|
@ -305,7 +225,7 @@ export const CodeHighlighted = ({ children, highlight, lang }) => {
|
|||
return <>{html}</>
|
||||
}
|
||||
|
||||
export class Code extends React.Component {
|
||||
export default class Code extends React.Component {
|
||||
static defaultProps = {
|
||||
lang: 'none',
|
||||
executable: null,
|
||||
|
@ -354,6 +274,8 @@ export class Code extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const JuniperDynamic = dynamic(() => import('./juniper'))
|
||||
|
||||
const JuniperWrapper = ({ title, lang, children }) => {
|
||||
const { binderUrl, binderVersion } = siteMetadata
|
||||
const juniperTitle = title || 'Editable Code'
|
||||
|
@ -369,7 +291,7 @@ const JuniperWrapper = ({ title, lang, children }) => {
|
|||
</span>
|
||||
</h4>
|
||||
|
||||
<Juniper
|
||||
<JuniperDynamic
|
||||
repo={binderUrl}
|
||||
branch={binderBranch}
|
||||
lang={lang}
|
||||
|
@ -381,7 +303,7 @@ const JuniperWrapper = ({ title, lang, children }) => {
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</Juniper>
|
||||
</JuniperDynamic>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
14
website/src/components/codeBlock.js
Normal file
14
website/src/components/codeBlock.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from 'react'
|
||||
import Code from './codeDynamic'
|
||||
import classes from '../styles/code.module.sass'
|
||||
|
||||
export const Pre = (props) => {
|
||||
return <pre className={classes['pre']}>{props.children}</pre>
|
||||
}
|
||||
|
||||
const CodeBlock = (props) => (
|
||||
<Pre>
|
||||
<Code {...props} />
|
||||
</Pre>
|
||||
)
|
||||
export default CodeBlock
|
5
website/src/components/codeDynamic.js
Normal file
5
website/src/components/codeDynamic.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
export default dynamic(() => import('./code'), {
|
||||
loading: () => <div style={{ color: 'white', padding: '1rem' }}>Loading...</div>,
|
||||
})
|
|
@ -5,8 +5,8 @@ import ImageNext from 'next/image'
|
|||
|
||||
import Link from './link'
|
||||
import Button from './button'
|
||||
import { InlineCode } from './code'
|
||||
import { MarkdownToReact } from './util'
|
||||
import { InlineCode } from './inlineCode'
|
||||
import MarkdownToReact from './markdownToReactDynamic'
|
||||
|
||||
import classes from '../styles/embed.module.sass'
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import classNames from 'classnames'
|
|||
import Icon from './icon'
|
||||
import Link from './link'
|
||||
import classes from '../styles/code.module.sass'
|
||||
import { Code } from './code'
|
||||
import Code from './codeDynamic'
|
||||
|
||||
const defaultErrorMsg = `Can't fetch code example from GitHub :(
|
||||
|
||||
|
|
12
website/src/components/htmlToReact.js
Normal file
12
website/src/components/htmlToReact.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Parser as HtmlToReactParser } from 'html-to-react'
|
||||
|
||||
const htmlToReactParser = new HtmlToReactParser()
|
||||
/**
|
||||
* Convert raw HTML to React elements
|
||||
* @param {string} html - The HTML markup to convert.
|
||||
* @returns {Node} - The converted React elements.
|
||||
*/
|
||||
|
||||
export default function HtmlToReact(props) {
|
||||
return htmlToReactParser.parse(props.children)
|
||||
}
|
23
website/src/components/inlineCode.js
Normal file
23
website/src/components/inlineCode.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classNames from 'classnames'
|
||||
import { isString } from './util'
|
||||
import classes from '../styles/code.module.sass'
|
||||
|
||||
const WRAP_THRESHOLD = 30
|
||||
|
||||
export const InlineCode = ({ wrap = false, className, children, ...props }) => {
|
||||
const codeClassNames = classNames(classes['inline-code'], className, {
|
||||
[classes['wrap']]: wrap || (isString(children) && children.length >= WRAP_THRESHOLD),
|
||||
})
|
||||
return (
|
||||
<code className={codeClassNames} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
InlineCode.propTypes = {
|
||||
wrap: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
}
|
|
@ -11,7 +11,7 @@ import overlayLegacy from '../images/pattern_landing_legacy.png'
|
|||
import Grid from './grid'
|
||||
import { Content } from './main'
|
||||
import Button from './button'
|
||||
import CodeBlock from './code'
|
||||
import CodeBlock from './codeBlock'
|
||||
import { H1, H2, H3 } from './typography'
|
||||
import Link from './link'
|
||||
import classes from '../styles/landing.module.sass'
|
||||
|
|
32
website/src/components/markdownToReact.js
Normal file
32
website/src/components/markdownToReact.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import remarkPlugins from '../../plugins/index.mjs'
|
||||
|
||||
/**
|
||||
* Convert raw Markdown to React
|
||||
* @param {String} markdown - The Markdown markup to convert.
|
||||
* @param {Object} [remarkReactComponents] - Optional React components to use
|
||||
* for HTML elements.
|
||||
* @returns {Node} - The converted React elements.
|
||||
*/
|
||||
export default function MarkdownToReact({ markdown }) {
|
||||
const [mdx, setMdx] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
const getMdx = async () => {
|
||||
setMdx(
|
||||
await serialize(markdown, {
|
||||
parseFrontmatter: false,
|
||||
mdxOptions: {
|
||||
remarkPlugins,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getMdx()
|
||||
}, [markdown])
|
||||
|
||||
return mdx ? <MDXRemote {...mdx} /> : <></>
|
||||
}
|
5
website/src/components/markdownToReactDynamic.js
Normal file
5
website/src/components/markdownToReactDynamic.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
export default dynamic(() => import('./markdownToReact'), {
|
||||
loading: () => <p>Loading...</p>,
|
||||
})
|
|
@ -5,7 +5,7 @@ import classNames from 'classnames'
|
|||
import Button from './button'
|
||||
import Tag from './tag'
|
||||
import { OptionalLink } from './link'
|
||||
import { InlineCode } from './code'
|
||||
import { InlineCode } from './inlineCode'
|
||||
import { H1, Label, InlineList, Help } from './typography'
|
||||
import Icon from './icon'
|
||||
|
||||
|
|
51
website/src/components/typeAnnotation.js
Normal file
51
website/src/components/typeAnnotation.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import CUSTOM_TYPES from '../../meta/type-annotations.json'
|
||||
import Link from './link'
|
||||
import classes from '../styles/code.module.sass'
|
||||
|
||||
export const WRAP_THRESHOLD = 30
|
||||
|
||||
const specialCharacterList = ['[', ']', ',', ', ']
|
||||
|
||||
const highlight = (element) =>
|
||||
specialCharacterList.includes(element) ? (
|
||||
<span className={classes['cli-arg-subtle']}>{element}</span>
|
||||
) : (
|
||||
element
|
||||
)
|
||||
|
||||
function linkType(el, showLink = true, key) {
|
||||
if (!el.length) return el
|
||||
const elStr = el.trim()
|
||||
if (!elStr) return el
|
||||
const typeUrl = CUSTOM_TYPES[elStr]
|
||||
const url = typeUrl == true ? DEFAULT_TYPE_URL : typeUrl
|
||||
return url && showLink ? (
|
||||
<Link to={url} hideIcon key={key}>
|
||||
{elStr}
|
||||
</Link>
|
||||
) : (
|
||||
highlight(el)
|
||||
)
|
||||
}
|
||||
|
||||
export const TypeAnnotation = ({ lang = 'python', link = true, children }) => {
|
||||
const code = Array.isArray(children) ? children.join('') : children || ''
|
||||
const [rawText, meta] = code.split(/(?= \(.+\)$)/)
|
||||
const annotClassNames = classNames(
|
||||
'type-annotation',
|
||||
`language-${lang}`,
|
||||
classes['inline-code'],
|
||||
classes['type-annotation'],
|
||||
{
|
||||
[classes['wrap']]: code.length >= WRAP_THRESHOLD,
|
||||
}
|
||||
)
|
||||
return (
|
||||
<span className={annotClassNames} role="code" aria-label="Type annotation">
|
||||
{rawText.split(/(\[|\]|,)/).map((el, i) => linkType(el, !!link, i))}
|
||||
{meta && <span className={classes['type-annotation-meta']}>{meta}</span>}
|
||||
</span>
|
||||
)
|
||||
}
|
|
@ -1,12 +1,6 @@
|
|||
import React, { Fragment, useEffect, useState } from 'react'
|
||||
import { Parser as HtmlToReactParser } from 'html-to-react'
|
||||
import React, { Fragment } from 'react'
|
||||
import siteMetadata from '../../meta/site.json'
|
||||
import { domain } from '../../meta/dynamicMeta.mjs'
|
||||
import remarkPlugins from '../../plugins/index.mjs'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
|
||||
const htmlToReactParser = new HtmlToReactParser()
|
||||
|
||||
const isNightly = siteMetadata.nightlyBranches.includes(domain)
|
||||
export const DEFAULT_BRANCH = isNightly ? 'develop' : 'master'
|
||||
|
@ -70,43 +64,6 @@ export function isEmptyObj(obj) {
|
|||
return Object.entries(obj).length === 0 && obj.constructor === Object
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert raw HTML to React elements
|
||||
* @param {string} html - The HTML markup to convert.
|
||||
* @returns {Node} - The converted React elements.
|
||||
*/
|
||||
export function htmlToReact(html) {
|
||||
return htmlToReactParser.parse(html)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert raw Markdown to React
|
||||
* @param {String} markdown - The Markdown markup to convert.
|
||||
* @param {Object} [remarkReactComponents] - Optional React components to use
|
||||
* for HTML elements.
|
||||
* @returns {Node} - The converted React elements.
|
||||
*/
|
||||
export function MarkdownToReact({ markdown }) {
|
||||
const [mdx, setMdx] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
const getMdx = async () => {
|
||||
setMdx(
|
||||
await serialize(markdown, {
|
||||
parseFrontmatter: false,
|
||||
mdxOptions: {
|
||||
remarkPlugins,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getMdx()
|
||||
}, [markdown])
|
||||
|
||||
return mdx ? <MDXRemote {...mdx} /> : <></>
|
||||
}
|
||||
|
||||
/**
|
||||
* Join an array of nodes with a given string delimiter, like Array.join for React
|
||||
* @param {Array} arr - The elements to join.
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import Link from './components/link'
|
||||
import Section, { Hr } from './components/section'
|
||||
import { Table, Tr, Th, Tx, Td } from './components/table'
|
||||
import CodeBlock, { Pre, Code, InlineCode, TypeAnnotation } from './components/code'
|
||||
import Code from './components/codeDynamic'
|
||||
import { TypeAnnotation } from './components/typeAnnotation'
|
||||
import { InlineCode } from './components/inlineCode'
|
||||
import CodeBlock, { Pre } from './components/codeBlock'
|
||||
import { Ol, Ul, Li } from './components/list'
|
||||
import { H2, H3, H4, H5, P, Abbr, Help, Label } from './components/typography'
|
||||
import Accordion from './components/accordion'
|
||||
|
|
|
@ -13,7 +13,7 @@ import Progress from '../components/progress'
|
|||
import Footer from '../components/footer'
|
||||
import SEO from '../components/seo'
|
||||
import Link from '../components/link'
|
||||
import { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import Alert from '../components/alert'
|
||||
import Search from '../components/search'
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import Title from '../components/title'
|
|||
import Section from '../components/section'
|
||||
import Button from '../components/button'
|
||||
import Aside from '../components/aside'
|
||||
import CodeBlock, { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import CodeBlock from '../components/codeBlock'
|
||||
import { Table, Tr, Td, Th } from '../components/table'
|
||||
import Tag from '../components/tag'
|
||||
import { H2, Label } from '../components/typography'
|
||||
|
@ -13,14 +14,8 @@ import Icon from '../components/icon'
|
|||
import Link, { OptionalLink } from '../components/link'
|
||||
import Infobox from '../components/infobox'
|
||||
import Accordion from '../components/accordion'
|
||||
import {
|
||||
isString,
|
||||
isEmptyObj,
|
||||
join,
|
||||
arrayToObj,
|
||||
abbrNum,
|
||||
MarkdownToReact,
|
||||
} from '../components/util'
|
||||
import { isString, isEmptyObj, join, arrayToObj, abbrNum } from '../components/util'
|
||||
import MarkdownToReact from '../components/markdownToReactDynamic'
|
||||
|
||||
import siteMetadata from '../../meta/site.json'
|
||||
import languages from '../../meta/languages.json'
|
||||
|
|
|
@ -8,7 +8,8 @@ import Grid from '../components/grid'
|
|||
import Button from '../components/button'
|
||||
import Icon from '../components/icon'
|
||||
import Tag from '../components/tag'
|
||||
import CodeBlock, { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import CodeBlock from '../components/codeBlock'
|
||||
import Aside from '../components/aside'
|
||||
import Sidebar from '../components/sidebar'
|
||||
import Section, { Hr } from '../components/section'
|
||||
|
@ -16,7 +17,8 @@ import Main from '../components/main'
|
|||
import Footer from '../components/footer'
|
||||
import { H3, H5, Label, InlineList } from '../components/typography'
|
||||
import { YouTube, SoundCloud, Iframe } from '../components/embed'
|
||||
import { github, MarkdownToReact } from '../components/util'
|
||||
import { github } from '../components/util'
|
||||
import MarkdownToReact from '../components/markdownToReactDynamic'
|
||||
|
||||
import { nightly, legacy } from '../../meta/dynamicMeta.mjs'
|
||||
import universe from '../../meta/universe.json'
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, Fragment } from 'react'
|
|||
import { window } from 'browser-monads'
|
||||
|
||||
import Link from '../components/link'
|
||||
import { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import { Label, H3 } from '../components/typography'
|
||||
import { Table, Tr, Th, Td } from '../components/table'
|
||||
import Infobox from '../components/infobox'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
|
||||
import Link from '../components/link'
|
||||
import { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import { Table, Tr, Th, Td } from '../components/table'
|
||||
import { Ul, Li } from '../components/list'
|
||||
import Infobox from '../components/infobox'
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react'
|
|||
import CopyInput from '../components/copy'
|
||||
import Infobox from '../components/infobox'
|
||||
import Link from '../components/link'
|
||||
import { InlineCode } from '../components/code'
|
||||
import { InlineCode } from '../components/inlineCode'
|
||||
import { projectsRepo } from '../components/util'
|
||||
|
||||
const COMMAND = 'python -m spacy project clone'
|
||||
|
|
|
@ -5,8 +5,8 @@ import 'prismjs/components/prism-ini.min.js'
|
|||
|
||||
import { Quickstart } from '../components/quickstart'
|
||||
import generator, { DATA as GENERATOR_DATA } from './quickstart-training-generator'
|
||||
import { htmlToReact } from '../components/util'
|
||||
import models from '../../meta/languages.json'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const DEFAULT_LANG = 'en'
|
||||
const DEFAULT_HARDWARE = 'cpu'
|
||||
|
@ -70,6 +70,10 @@ const DATA = [
|
|||
},
|
||||
]
|
||||
|
||||
const HtmlToReactDynamic = dynamic(() => import('../components/htmlToReact'), {
|
||||
loading: () => <></>,
|
||||
})
|
||||
|
||||
export default function QuickstartTraining({ id, title, download = 'base_config.cfg' }) {
|
||||
const [lang, setLang] = useState(DEFAULT_LANG)
|
||||
const [_components, _setComponents] = useState([])
|
||||
|
@ -134,7 +138,7 @@ export default function QuickstartTraining({ id, title, download = 'base_config.
|
|||
small
|
||||
codeLang="ini"
|
||||
>
|
||||
{htmlToReact(displayContent)}
|
||||
<HtmlToReactDynamic>{displayContent}</HtmlToReactDynamic>
|
||||
</Quickstart>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user