import React, { Component, ReactElement, ReactNode, useContext } from 'react' import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants' import type { DocumentContext, DocumentInitialProps, DocumentProps, } from '../shared/lib/utils' import { BuildManifest, getPageFiles } from '../server/get-page-files' import { cleanAmpPath } from '../server/utils' import { htmlEscapeJsonString } from '../server/htmlescape' import Script, { ScriptProps } from '../client/script' import isError from '../lib/is-error' import { HtmlContext } from '../shared/lib/html-context' import type { HtmlProps } from '../shared/lib/html-context' export { DocumentContext, DocumentInitialProps, DocumentProps } export type OriginProps = { nonce?: string crossOrigin?: string children?: React.ReactNode } type DocumentFiles = { sharedFiles: readonly string[] pageFiles: readonly string[] allFiles: readonly string[] } function getDocumentFiles( buildManifest: BuildManifest, pathname: string, inAmpMode: boolean ): DocumentFiles { const sharedFiles: readonly string[] = getPageFiles(buildManifest, '/_app') const pageFiles: readonly string[] = inAmpMode ? [] : getPageFiles(buildManifest, pathname) return { sharedFiles, pageFiles, allFiles: [...new Set([...sharedFiles, ...pageFiles])], } } function getPolyfillScripts(context: HtmlProps, props: OriginProps) { // polyfills.js has to be rendered as nomodule without async // It also has to be the first script to load const { assetPrefix, buildManifest, devOnlyCacheBusterQueryString, disableOptimizedLoading, crossOrigin, } = context return buildManifest.polyfillFiles .filter( (polyfill) => polyfill.endsWith('.js') && !polyfill.endsWith('.module.js') ) .map((polyfill) => ( )) } function hasComponentProps(child: any): child is React.ReactElement { return !!child && !!child.props } function getPreNextWorkerScripts(context: HtmlProps, props: OriginProps) { const { assetPrefix, scriptLoader, crossOrigin, nextScriptWorkers } = context // disable `nextScriptWorkers` in edge runtime if (!nextScriptWorkers || process.env.NEXT_RUNTIME === 'edge') return null try { let { partytownSnippet, // @ts-ignore: Prevent webpack from processing this require } = __non_webpack_require__('@builder.io/partytown/integration'!) const children = Array.isArray(props.children) ? props.children : [props.children] // Check to see if the user has defined their own Partytown configuration const userDefinedConfig = children.find( (child) => hasComponentProps(child) && child?.props?.dangerouslySetInnerHTML?.__html.length && 'data-partytown-config' in child.props ) return ( <> {!userDefinedConfig && ( )} {(scriptLoader.worker || []).map((file: ScriptProps, index: number) => { const { strategy, ...scriptProps } = file return ( ) })} > ) } catch (err) { if (isError(err) && err.code !== 'MODULE_NOT_FOUND') { console.warn( `Warning: Partytown could not be instantiated in your application due to an error. ${err.message}` ) } return null } } function getPreNextScripts(context: HtmlProps, props: OriginProps) { const { scriptLoader, disableOptimizedLoading, crossOrigin } = context const webWorkerScripts = getPreNextWorkerScripts(context, props) const beforeInteractiveScripts = (scriptLoader.beforeInteractive || []).map( (file: ScriptProps, index: number) => { const { strategy, ...scriptProps } = file return ( ) } ) return ( <> {webWorkerScripts} {beforeInteractiveScripts} > ) } function getDynamicChunks( context: HtmlProps, props: OriginProps, files: DocumentFiles ) { const { dynamicImports, assetPrefix, isDevelopment, devOnlyCacheBusterQueryString, disableOptimizedLoading, crossOrigin, } = context return dynamicImports.map((file) => { if (!file.endsWith('.js') || files.allFiles.includes(file)) return null return ( ) }) } function getScripts( context: HtmlProps, props: OriginProps, files: DocumentFiles ) { const { assetPrefix, buildManifest, isDevelopment, devOnlyCacheBusterQueryString, disableOptimizedLoading, crossOrigin, } = context const normalScripts = files.allFiles.filter((file) => file.endsWith('.js')) const lowPriorityScripts = buildManifest.lowPriorityFiles?.filter((file) => file.endsWith('.js') ) return [...normalScripts, ...lowPriorityScripts].map((file) => { return ( ) }) } /** * `Document` component handles the initial `document` markup and renders only on the server side. * Commonly used for implementing server side rendering for `css-in-js` libraries. */ export default class Document
extends Component