react-static
Version:
A progressive static site generator for React
654 lines (588 loc) • 20.4 kB
TypeScript
// Type definitions for react-static 7.1.0
// Project: https://github.com/react-static/react-static
// Definitions by: Various Contributors https://github.com/react-static/react-static/blame/master/packages/react-static/src/index.d.ts
//
// VERY lightly maintained, we need all the help we can get
import * as React from 'react';
import { Configuration as WebpackDevServerConfig } from 'webpack-dev-server';
import { RenderFn } from 'create-react-context';
export { Helmet as Head } from 'react-helmet'
interface StaticInfo<T extends object> {
path: string
siteData: T
}
interface RoutesRenderProp {
routePath: string
getComponentForPath(pathname: string): React.ReactNode | false
}
interface RouteDataProps<T extends object> {
children: (routeData: T) => React.ReactNode
}
interface SiteDataProps<T extends object> {
children: (siteData: T) => React.ReactNode
}
/**
* Hook to get the Route Data for the current route
*
* Via `React.Suspense`, the `useRouteData` hook asynchronously provides the
* results of a routes's `getData` as defined in your `static.config.js`
*
* @see Route
* @see Routes
* @see routePathContext
*
* This will match the `getData()` result from `Route` and
* only works if it is called in a child of `routePathContext.Provider`, which
* is always the case in a child of the `Routes` component:
* - any template/container (the `template` key in `Route`)
* - any page via the react-static-plugin-source-filesystem
* - if you manually wrap a component with `<routePathContext.Provider>`
*
* Alternatives include the `RouteData` component and the `withRouteData` HOC.
*
* @example Getting the route data inside a container (`template`)
*
* function PostContainer(): JSX.Element {
* const postData = useRouteData<RouteData>()
* // => { the post data }
* }
*
* @example Getting the route data _oustide_ the container / page / `Routes`
*
* // This needs additional code to work properly in production, because there
* // the useStaticInfo must be used to "hydrate" this data as Suspending is
* // not allowed in production.
*
* // You might just want to use `render` prop in `Routes` instead.
*
* function Outside(): JSX.Element {
* const currentPath = getRoutePath(location.pathName)
* return <routePathContext.Provider value={currentPath}>
* {childThatCallsUseRouteData}
* </routePathContext.Provider>
* }
*
* @returns {T} the route data
*/
export function useRouteData<T extends object = any>(): T;
/**
* Hook to get the Site Data
*
* Via `React.Suspense`, the `useSiteData` hook asynchronously provides the
* results of the `getSiteData` function as defined in your `static.config.js`.
*
* @see ReactStaticConfig
*
* Alternatives include the `SiteData` component and the `withSiteData` HOC.
*
* @template T the type of the site data
* @returns {T} the side data
*/
export function useSiteData<T extends object = any>(): T;
/**
* Hook to prefetch a path as soon as the linked ref becomes visible.
*
* @param {string} path the path to pre-fetch
* @param {React.Ref} [ref]
* @returns {React.Ref} returns the prefetch-ref
*
* The `usePrefetch` hook binds the prefetching of a specific path's assets to
* the visibility of an element. When the ref's element becomes visible in the
* viewport, the template and data required to render the route for the `path`
* will be prefetched.
*
* @example Using the prefetch hook to prefetch a link
*
* import { useRef } from 'react'
* import { usePrefetch } from 'react-static'
* export default () => {
* // Use it to create a ref
* const myRef = usePrefetch('/blog')
*
* // or pass your own ref
* const myRef = useRef()
* usePrefetch('./blog', myRef)
*
* return (<Link to="/blog" ref={myRef}>Go to blog</Link>)
* }
*
*/
export function usePrefetch<T extends object = any>(
path: string,
ref?: React.MutableRefObject<T>
): React.MutableRefObject<T>;
/**
* Hook to get the current Location object (or `undefined` on the first render)
*
* Makes a component rerender each time the `Location` changes.
*
* @returns {(Location | undefined)} the current location
*/
export function useLocation(): Location | undefined
/**
* Hook to get the mounted base path
*
* @returns {string} empty string when REACT_STATIC_DISABLE_ROUTE_PREFIXING
* is true, REACT_STATIC_BASE_PATH otherwise.
*/
export function useBasepath(): string;
/**
* Primarily used during the build process and to hydrate the app from static
* data. You probably don't need this when writing your app using `react-static`
*
* @template T the siteData
* @returns {(StaticInfo<T> | undefined)} the static info, if any
*/
export function useStaticInfo<T extends object = any>(): StaticInfo<T> | undefined;
/**
* Hook to **clean** a route path. Returns the context value if inside a context
* or sanitizes via `getRoutePath` otherwise.
*
* @param {string} routePath
* @returns {string}
*
* Once a routePath goes into the `Routes` component, `useRoutePath` must ALWAYS
* return the `routePath` used in its parent, so the `Routes` component always
* passes it down inside a context
*
* @see getRoutePath
* @see routePathContext
*/
export function useRoutePath(routePath: string): string;
/**
* Context to provide the current route path.
*
* This is used in the `Routes` component and provides the route path to
* `useRoutePath`. If you want to use that hook outside of the `Routes`
* component, e.g. a sidebar, you can use this context's Provider to pass the
* current route down.
*
* @see useRouteData
* @see useRoutePath
*
* The `useRouteData` hook has an example how to do this properly.
*/
export const routePathContext: React.Context<string>;
/**
* HOC that passes the `routeData` as a prop to the wrapped `Component`
*
* @see useRouteData
*
* @template T type of the routeData
* @template P type of the props of the original `Component`
* @template S type of the state of the original `Component`
*
* @param {React.ComponentType<P>} Comp the component to wrap
* @returns {(React.ComponentClass<P & { routeData: T }, S>)} tbe wrapped component
*/
export function withRouteData<T extends object = any, P extends object = {}, S extends object = {}>(
Comp: React.ComponentType<P>
): React.ComponentClass<P & { routeData: T }, S>;
/**
* Component with a render function prop as children, which receives the current
* `routeData` as its only argument.
*
* @see useRouteData
*
* @template T type of the routedata
* @param {{ children: (routeData: T) => React.ReactNode }} props
* @returns {React.ReactNode}
*/
export function RouteData<T extends object = any>(
props: RouteDataProps<T>
): React.ReactNode
/**
* HOC that passes the `siteData` as a prop to the wrapped `Component`
*
* @see useSiteData
*
* @template T type of the siteData
* @template P type of the props of the original `Component`
* @template S type of the state of the original `Component`
*
* @param {React.ComponentType<P>} Comp the component to wrap
* @returns {(React.ComponentClass<P & { siteData: T }, S>)} tbe wrapped component
*/
export function withSiteData<T extends object = any, P extends object = {}, S extends object = {}>(
Comp: React.ComponentType<P>
): React.ComponentClass<P & { routeData: T }, S>;
/**
* Component with a render function prop as children, which receives the current
* `siteData` as its only argument.
*
* @see useSiteData
*
* @template T type of the routedata
* @param {{ children: (siteData: T) => React.ReactNode }} props
* @returns {React.ReactNode}
*/
export function SiteData<T extends object = any>(
props: SiteDataProps<T>
): React.ReactNode
/**
* The root component for a `react-static` app
*/
export class Root extends React.Component {}
/**
* Routes component to render all routes
*
* React Static handles all of your routing for you using a router under the
* hood. All you need to do is import `Routes` and specify where you want to
* render them. (see first example).
*
* Occasionally, you may need to render the automatic `<Routes>` component in a
* custom way. The most common use-case is illustrated in the animated-routes
* example transitions. To do this, utilize a render prop. (see second example).
*
* @example handle all the routes in `react-static`
*
* import { Root, Routes } from 'react-static'
* export default () => (<Root>
* <Router>
* <Routes path="*" />
* </Router>
* </Root>)
*
* @example handle the routes manually
*
* export default () => (<Root>
* <Routes>
* {({ routePath, getComponentForPath }) => {
* // The pathname is used to retrieve the component for that path
* let Comp = getComponentForPath(routePath)
* // The component is rendered!
* return <Comp />
* }}
* </Routes>
* </Root>)
*
*/
export class Routes extends React.Component<{
path?: string,
default?: boolean,
render?: (props: RoutesRenderProp) => React.ReactNode
}> {}
// Export all from the browser types; since those are the only things typed
export * from './browser'
type AnyReactComponent = React.ComponentType<Record<string, any>>
/**
* @see https://github.com/react-static/react-static/blob/master/docs/config.md
*/
export interface ReactStaticConfig {
/**
* Your siteRoot in the format of `protocol://domain.com` is highly
* recommended and is necessary for many things related to SEO to function
* for your site. So far, this includes:
*
* - Automatically generating a `sitemap.xml` on export
* - Forcing absolute URLs in statically rendered links. Make sure that you
* include https if you serve your site with it (which we highly recommend).
* **Any trailing slashes including the pathname will be removed automatically**.
* If you need to set a base path for your site (eg. if you're using GitHub
* pages), you'll want to use the `basePath` option.
*/
siteRoot?: string
/**
* Works exactly like `siteRoot`, but only when building with the
* `--staging` build flag.
*/
stagingSiteRoot?: string
/**
* Your basePath in the format of some/route is necessary if you intend on
* hosting your app from a specific route on your domain (eg. When using
* Github Pages or for example: https://mysite.com/blog where blog would the
* `basePath`)
*
* **All leading and trailing slashes are removed automatically**.
*/
basePath?: string
/**
* Works exactly like `basePath`, but only when building with the
* `--staging` build flag.
*/
stagingBasePath?: string
/**
* Works exactly like `basePath`, but only when running the dev server.
*/
devBasePath?: string
/**
* Your `assetsPath` determines where your bundled JS and CSS will be loaded
* from. This is helpful if you want to host your assets in an external
* location such as a CDN.
*/
assetsPath?: string
/**
* `extractCssChunks` replaces default `ExtractTextPlugin` with
* `ExtractCssChunks`. It enables automatic CSS splitting into separate files
* by routes as well as dynamic components (using
* `react-universal-component`).
*
* @see https://github.com/faceyspacey/extract-css-chunks-webpack-plugin
* More information about the plugin and why it is useful as a part of CSS
* delivery optimisation.
*
* @default false
*/
extractCssChunks?: boolean
/**
* By using `extractCssChunks` option and putting code splitting at
* appropriate places, your page related CSS file can be minimal. This option
* allows you to inline your page related CSS in order to speed up your
* application by reducing the number of requests required for a first paint.
*
* @default false
*/
inlineCss?: boolean
/**
* Set this boolean to true to disable all preloading. This is mostly meant
* for debugging at this point, but the internal mechanics could soon be
* converted into a condition to either preload or not based on the client
* (mobile, slow-connection, etc)
*
* @default false
*/
disablePreload?: boolean
/**
* The maximum number of files that can be concurrently written to disk during
* the build process.
*/
outputFileRate?: number
/**
* The maximum number of inflight requests for preloading route data on the
* client.
*/
prefetchRate?: number
/**
* An optional Number of maximum threads to use when exporting your site's
* pages. By default this is set to Infinity to use all available threads on
* the machine React Static is running on.
*
* _NOTE: This only affects the process that are rendering your pages to html
* files, not the initial bundling process._
*/
maxThreads?: number
/**
* An optional Number of milliseconds to show the loading spinner when
* templates, siteData or routeData are not immediately available. If you are
* preloading aggressively, you shouldn't see a loader at all, but if a loader
* is shown, it's a good user experience to make is as un-flashy as possible.
*/
minLoadTime?: number
/**
* Set to true to disable prefixing link href values and the browser history
* with `config.basePath`. Useful if you are using a variable basePath such as
* `/country/language/basePath`.
*
* @default false
*/
disableRoutePrefixing?: boolean
/**
* Set to true to disable warnings of duplicate routes during builds.
*
* @default false
*/
disableDuplicateRoutesWarning?: boolean
/**
* An object of internal directories used by react-static that can be customized.
* Each path is relative to your project root.
*/
paths?: PathsConfig
/**
* We are running Babel seperately for your own sources and externals. The
* Babel configuration for your own sources can be manipulated the normal way.
* The one for `node_modules` can not, since it's a bit special. We try to
* compile them with a bare minimum, but sometimes some modules gives you
* trouble (e.g. `mapbox-gl`)
*
* This option gives you the ability to exclude some modules from babelifying.
* See https://webpack.js.org/configuration/module/#condition for more
* details.
*
* @example To exclude e.g. `mapboxgl`
*
* export default {
* babelExcludes: [/mapbox-gl/],
* }
*/
babelExcludes?: RegExp[]
/**
* An `Object` of options to be passed to the underlying
* `webpack-dev-server` instance used for development.
*/
devServer?: WebpackDevServerConfig
/**
* Set this flag to true to include source maps in production.
*
* @default false
*/
productionSourceMaps?: boolean
/**
* It's never been easier to customize the root document of your website!
* `Document` is an optional (and again, recommended) react component
* responsible for rendering the HTML shell of your website.
*
* Things you may want to place here:
*
* - Site-wide custom head and/or meta tags
* - Site-wide analytics scripts
* - Site-wide stylesheets
*/
Document?: DocumentComponent
/**
* An asynchronous function that should resolve an array of route objects.
* You'll probably want to use this function to request any dynamic data or
* information that is needed to build all of the routes for your site. It is
* also passed an object containing a dev boolean indicating whether it's
* being run in a production build or not.
*
* @param {RouteFlags} flags
* @returns {Promise<Route[]>} the promise that resolves the site data
*/
getRoutes?(flags: RouteFlags): Promise<Route[]>
/**
* `getSiteData` is very similar to a route's getData function, but its result
* is made available to the entire site via the `SiteData` and `useSiteData`
* component/HOC. Any data you return here, although loaded once per session,
* will be embedded in every page that is exported on your site. So tread
* lightly ;)
*
* @template T the type of the site data
* @param {RouteFlags} flags
* @returns {Promise<T>} the promise that resolves the site data
*/
getSiteData?(flags: RouteFlags): Promise<object> | object
plugins?: Array<PluginConfiguration>
entry?: string
}
export type PluginConfiguration = string | [string, object?]
export interface RouteFlags {
stage: 'dev' | 'prod'
debug?: boolean
isBuildCommand?: boolean
staging?: boolean
incremental?: boolean
}
/**
* A route is an object that represents a unique location in your site and is
* the backbone of every React-Static site.
*/
export interface Route {
/**
* The path of the URL to match for this route, excluding search parameters
* and hash fragments, relative to your siteRoot + basePath (if this is a
* child route, also relative to this route's parent path)
*/
path: string
/**
* The path of the component to be used to render this route. (Relative to the
* root of your project)
*/
template?: string
/**
* Setting this to a URL will perform the equivalent of a 301 redirect (as
* much as is possible within a static site) using http-equiv meta tags,
* canonicals, etc. This will force the page to render only the bare minimum
* to perform the redirect and nothing else.
*/
redirect?: URL | string
/**
* Routes can and should have nested routes when necessary. Route paths are
* inherited as they are nested, so there is no need to repeat a path prefix
* in nested routes.
*/
children?: Route[]
/**
* An async function that returns or resolves an object of any necessary data
* for this route to render.
*
* @template T type of the route data
* @param {Route} resolvedRoute This is the resolved route this function is handling.
* @param {RouteFlags} flags An object of flags and meta information about the build
* @returns {T | Promise<T>} the route data
*/
getData?(resolvedRoute: Route, flags: RouteFlags): Promise<object> | object
replace?: boolean
}
export interface PathsConfig {
/** @default process.cwd() */
root?: string
/** @default 'src' */
src?: string
/** @default 'tmp' */
temp?: string
/** @default 'dist' */
dist?: string
/** @default 'tmp/dev-server' */
devDist?: string
/** @default 'public' */
public?: string
/** @default 'dist' */
assets?: string
/** @default 'artifacts' */
buildArtifacts?: string
pages?: string
plugins?: string
nodeModules?: string
}
export type DocumentComponent<T extends object = any> = React.ComponentType<DocumentProps<T>>
export interface InlineScript {
/**
* The script as a string.
*/
script: string
/**
* The script's sha256 hash in base64 using the subresource integrity format,
* eg.: 'sha256-<base64-value>'. This value can be directly used in a CSP.
*/
hash: string
}
export interface InlineScripts extends Record<string, InlineScript> {
routeInfo: InlineScript
}
export interface DocumentProps<T extends object = any> {
Html: JSX.IntrinsicElements['html']
Head: JSX.IntrinsicElements['head']
Body: JSX.IntrinsicElements['body']
children: React.ReactNode
state: {
siteData: T
routeInfo: unknown
renderMeta: unknown,
inlineScripts: InlineScripts
}
}
export interface OnStartArgs {
devServerConfig: Readonly<WebpackDevServerConfig>
}
/**
* This function is for extracting a routePath from a path or string as
* RoutePaths do not have query params, basePaths, and should resemble the same
* string as passed in the static.config.js routes.
*
* You can use this to sanitize the pathname of a location before giving it to
* a custom `routePathContext.Provider`.
*
* @see routePathContext
*
* @param {string} routePath the unsanitized path
* @returns {string} the sanitized path
*/
export function getRoutePath(routePath: string): string
/**
* Utility function to turn any path into an absolute path
*
* @private
*
* @param {string} path the potentially non-absolute path
* @returns {string} the absolute equivalent
*/
export function makePathAbsolute(path: string): string
/**
* Utility function to join paths, making sure there are no double slashses
*
* @private
*
* @param {...ReadonlyArray<string>} paths the paths to join
* @returns {string} the joined path
*/
export function pathJoin(...paths: ReadonlyArray<string>): string