diff --git a/.eslintrc.json b/.eslintrc.json index b2508a20109d..cda24c1d8079 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -69,10 +69,10 @@ "warn", { "functions": false, - "classes": false, - "variables": false, - "enums": false, - "typedefs": false + "classes": true, + "variables": true, + "enums": true, + "typedefs": true } ], "no-unused-vars": "off", diff --git a/packages/next/build/analysis/extract-const-value.ts b/packages/next/build/analysis/extract-const-value.ts index e067b1c73eeb..a08c9d40b9a9 100644 --- a/packages/next/build/analysis/extract-const-value.ts +++ b/packages/next/build/analysis/extract-const-value.ts @@ -15,6 +15,8 @@ import type { VariableDeclaration, } from '@swc/core' +export class NoSuchDeclarationError extends Error {} + /** * Extracts the value of an exported const variable named `exportedName` * (e.g. "export const config = { runtime: 'experimental-edge' }") from swc's AST. @@ -138,7 +140,6 @@ export class UnsupportedValueError extends Error { this.path = codePath } } -export class NoSuchDeclarationError extends Error {} function extractValue(node: Node, path?: string[]): any { if (isNullLiteral(node)) { diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index af30649b7edc..d2692d1450d2 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -226,6 +226,7 @@ function getMiddlewareRegExpStrings( } } +let warnedAboutExperimentalEdgeApiFunctions = false function warnAboutExperimentalEdgeApiFunctions() { if (warnedAboutExperimentalEdgeApiFunctions) { return @@ -234,8 +235,6 @@ function warnAboutExperimentalEdgeApiFunctions() { warnedAboutExperimentalEdgeApiFunctions = true } -let warnedAboutExperimentalEdgeApiFunctions = false - const warnedUnsupportedValueMap = new Map() function warnAboutUnsupportedValue( pageFilePath: string, diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index f688d4e32e9d..76bca9ee85ed 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -135,6 +135,35 @@ function getCodeAnalizer(params: { } = params const { hooks } = parser + /** + * For an expression this will check the graph to ensure it is being used + * by exports. Then it will store in the module buildInfo a boolean to + * express that it contains dynamic code and, if it is available, the + * module path that is using it. + */ + const handleExpression = () => { + if (!isInMiddlewareLayer(parser)) { + return + } + + wp.optimize.InnerGraph.onUsage(parser.state, (used = true) => { + const buildInfo = getModuleBuildInfo(parser.state.module) + if (buildInfo.usingIndirectEval === true || used === false) { + return + } + + if (!buildInfo.usingIndirectEval || used === true) { + buildInfo.usingIndirectEval = used + return + } + + buildInfo.usingIndirectEval = new Set([ + ...Array.from(buildInfo.usingIndirectEval), + ...Array.from(used), + ]) + }) + } + /** * This expression handler allows to wrap a dynamic code expression with a * function call where we can warn about dynamic code not being allowed @@ -216,35 +245,6 @@ function getCodeAnalizer(params: { } } - /** - * For an expression this will check the graph to ensure it is being used - * by exports. Then it will store in the module buildInfo a boolean to - * express that it contains dynamic code and, if it is available, the - * module path that is using it. - */ - const handleExpression = () => { - if (!isInMiddlewareLayer(parser)) { - return - } - - wp.optimize.InnerGraph.onUsage(parser.state, (used = true) => { - const buildInfo = getModuleBuildInfo(parser.state.module) - if (buildInfo.usingIndirectEval === true || used === false) { - return - } - - if (!buildInfo.usingIndirectEval || used === true) { - buildInfo.usingIndirectEval = used - return - } - - buildInfo.usingIndirectEval = new Set([ - ...Array.from(buildInfo.usingIndirectEval), - ...Array.from(used), - ]) - }) - } - /** * Declares an environment variable that is being used in this module * through this static analysis. diff --git a/packages/next/client/future/image.tsx b/packages/next/client/future/image.tsx index 57e07805dd5f..10cac772bd82 100644 --- a/packages/next/client/future/image.tsx +++ b/packages/next/client/future/image.tsx @@ -319,6 +319,116 @@ function handleLoading( }) } +const ImageElement = ({ + imgAttributes, + heightInt, + widthInt, + qualityInt, + className, + imgStyle, + blurStyle, + isLazy, + fill, + placeholder, + loading, + srcString, + config, + unoptimized, + loader, + onLoadingCompleteRef, + setBlurComplete, + setShowAltText, + onLoad, + onError, + noscriptSizes, + ...rest +}: ImageElementProps) => { + loading = isLazy ? 'lazy' : loading + return ( + <> + { + if (process.env.NODE_ENV !== 'production') { + if (img && !srcString) { + console.error(`Image is missing required "src" property:`, img) + } + } + if (img?.complete) { + handleLoading( + img, + srcString, + placeholder, + onLoadingCompleteRef, + setBlurComplete + ) + } + }, + [srcString, placeholder, onLoadingCompleteRef, setBlurComplete] + )} + onLoad={(event) => { + const img = event.currentTarget as ImgElementWithDataProp + handleLoading( + img, + srcString, + placeholder, + onLoadingCompleteRef, + setBlurComplete + ) + if (onLoad) { + onLoad(event) + } + }} + onError={(event) => { + // if the real image fails to load, this will ensure "alt" is visible + setShowAltText(true) + if (placeholder === 'blur') { + // If the real image fails to load, this will still remove the placeholder. + setBlurComplete(true) + } + if (onError) { + onError(event) + } + }} + /> + {placeholder === 'blur' && ( + + )} + + ) +} + export default function Image({ src, sizes, @@ -690,116 +800,6 @@ export default function Image({ ) } -const ImageElement = ({ - imgAttributes, - heightInt, - widthInt, - qualityInt, - className, - imgStyle, - blurStyle, - isLazy, - fill, - placeholder, - loading, - srcString, - config, - unoptimized, - loader, - onLoadingCompleteRef, - setBlurComplete, - setShowAltText, - onLoad, - onError, - noscriptSizes, - ...rest -}: ImageElementProps) => { - loading = isLazy ? 'lazy' : loading - return ( - <> - { - if (process.env.NODE_ENV !== 'production') { - if (img && !srcString) { - console.error(`Image is missing required "src" property:`, img) - } - } - if (img?.complete) { - handleLoading( - img, - srcString, - placeholder, - onLoadingCompleteRef, - setBlurComplete - ) - } - }, - [srcString, placeholder, onLoadingCompleteRef, setBlurComplete] - )} - onLoad={(event) => { - const img = event.currentTarget as ImgElementWithDataProp - handleLoading( - img, - srcString, - placeholder, - onLoadingCompleteRef, - setBlurComplete - ) - if (onLoad) { - onLoad(event) - } - }} - onError={(event) => { - // if the real image fails to load, this will ensure "alt" is visible - setShowAltText(true) - if (placeholder === 'blur') { - // If the real image fails to load, this will still remove the placeholder. - setBlurComplete(true) - } - if (onError) { - onError(event) - } - }} - /> - {placeholder === 'blur' && ( - - )} - - ) -} - function defaultLoader({ config, src, diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 62cdcd9d84f3..20e17aff73e3 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -349,6 +349,120 @@ function handleLoading( }) } +const ImageElement = ({ + imgAttributes, + heightInt, + widthInt, + qualityInt, + layout, + className, + imgStyle, + blurStyle, + isLazy, + placeholder, + loading, + srcString, + config, + unoptimized, + loader, + onLoadingCompleteRef, + setBlurComplete, + setIntersection, + onLoad, + onError, + isVisible, + noscriptSizes, + ...rest +}: ImageElementProps) => { + loading = isLazy ? 'lazy' : loading + return ( + <> + { + if (process.env.NODE_ENV !== 'production') { + if (img && !srcString) { + console.error(`Image is missing required "src" property:`, img) + } + } + setIntersection(img) + if (img?.complete) { + handleLoading( + img, + srcString, + layout, + placeholder, + onLoadingCompleteRef, + setBlurComplete + ) + } + }, + [ + setIntersection, + srcString, + layout, + placeholder, + onLoadingCompleteRef, + setBlurComplete, + ] + )} + onLoad={(event) => { + const img = event.currentTarget as ImgElementWithDataProp + handleLoading( + img, + srcString, + layout, + placeholder, + onLoadingCompleteRef, + setBlurComplete + ) + if (onLoad) { + onLoad(event) + } + }} + onError={(event) => { + if (placeholder === 'blur') { + // If the real image fails to load, this will still remove the placeholder. + setBlurComplete(true) + } + if (onError) { + onError(event) + } + }} + /> + {(isLazy || placeholder === 'blur') && ( + + )} + + ) +} + export default function Image({ src, sizes, @@ -846,120 +960,6 @@ export default function Image({ ) } -const ImageElement = ({ - imgAttributes, - heightInt, - widthInt, - qualityInt, - layout, - className, - imgStyle, - blurStyle, - isLazy, - placeholder, - loading, - srcString, - config, - unoptimized, - loader, - onLoadingCompleteRef, - setBlurComplete, - setIntersection, - onLoad, - onError, - isVisible, - noscriptSizes, - ...rest -}: ImageElementProps) => { - loading = isLazy ? 'lazy' : loading - return ( - <> - { - if (process.env.NODE_ENV !== 'production') { - if (img && !srcString) { - console.error(`Image is missing required "src" property:`, img) - } - } - setIntersection(img) - if (img?.complete) { - handleLoading( - img, - srcString, - layout, - placeholder, - onLoadingCompleteRef, - setBlurComplete - ) - } - }, - [ - setIntersection, - srcString, - layout, - placeholder, - onLoadingCompleteRef, - setBlurComplete, - ] - )} - onLoad={(event) => { - const img = event.currentTarget as ImgElementWithDataProp - handleLoading( - img, - srcString, - layout, - placeholder, - onLoadingCompleteRef, - setBlurComplete - ) - if (onLoad) { - onLoad(event) - } - }} - onError={(event) => { - if (placeholder === 'blur') { - // If the real image fails to load, this will still remove the placeholder. - setBlurComplete(true) - } - if (onError) { - onError(event) - } - }} - /> - {(isLazy || placeholder === 'blur') && ( - - )} - - ) -} - function normalizeSrc(src: string): string { return src[0] === '/' ? src.slice(1) : src } diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 535584636276..9b5086028300 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -80,6 +80,7 @@ let headManager: { getIsSsr?: () => boolean } let initialMatchesMiddleware = false +let lastAppProps: AppProps let lastRenderReject: (() => void) | null let webpackHMR: any @@ -288,6 +289,18 @@ export async function initialize(opts: { webpackHMR?: any } = {}): Promise<{ return { assetPrefix: prefix } } +const wrapApp = + (App: AppComponent) => + (wrappedAppProps: Record): JSX.Element => { + const appProps: AppProps = { + ...wrappedAppProps, + Component: CachedComponent, + err: initialData.err, + router, + } + return {renderApp(App, appProps)} + } + export async function hydrate(opts?: { beforeRender?: () => Promise }) { let initialErr = initialData.err @@ -663,18 +676,6 @@ function renderApp(App: AppComponent, appProps: AppProps) { return } -const wrapApp = - (App: AppComponent) => - (wrappedAppProps: Record): JSX.Element => { - const appProps: AppProps = { - ...wrappedAppProps, - Component: CachedComponent, - err: initialData.err, - router, - } - return {renderApp(App, appProps)} - } - let RSCComponent: (props: any) => JSX.Element if (process.env.__NEXT_RSC) { const getCacheKey = () => { @@ -818,7 +819,6 @@ if (process.env.__NEXT_RSC) { } } -let lastAppProps: AppProps function doRender(input: RenderRouteInfo): Promise { let { App, Component, props, err, __N_RSC }: RenderRouteInfo = input let styleSheets: StyleSheetTuple[] | undefined = diff --git a/packages/next/client/use-intersection.tsx b/packages/next/client/use-intersection.tsx index 3f93811acaff..f13dd22f0b5c 100644 --- a/packages/next/client/use-intersection.tsx +++ b/packages/next/client/use-intersection.tsx @@ -72,6 +72,9 @@ export function useIntersection({ return [setElement, visible, resetVisible] } +const observers = new Map() +const idList: Identifier[] = [] + function observe( element: Element, callback: ObserveCallback, @@ -99,10 +102,6 @@ function observe( } } -const observers = new Map() - -const idList: Identifier[] = [] - function createObserver(options: UseIntersectionObserverInit): Observer { const id = { root: options.root || null, diff --git a/packages/next/lib/is-serializable-props.ts b/packages/next/lib/is-serializable-props.ts index e42407efc85f..77a8025e7559 100644 --- a/packages/next/lib/is-serializable-props.ts +++ b/packages/next/lib/is-serializable-props.ts @@ -5,6 +5,16 @@ import { const regexpPlainIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/ +export class SerializableError extends Error { + constructor(page: string, method: string, path: string, message: string) { + super( + path + ? `Error serializing \`${path}\` returned from \`${method}\` in "${page}".\nReason: ${message}` + : `Error serializing props returned from \`${method}\` in "${page}".\nReason: ${message}` + ) + } +} + export function isSerializableProps( page: string, method: string, @@ -131,13 +141,3 @@ export function isSerializableProps( return isSerializable(new Map(), input, '') } - -export class SerializableError extends Error { - constructor(page: string, method: string, path: string, message: string) { - super( - path - ? `Error serializing \`${path}\` returned from \`${method}\` in "${page}".\nReason: ${message}` - : `Error serializing props returned from \`${method}\` in "${page}".\nReason: ${message}` - ) - } -} diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index a5e24bed21dc..91268630de9b 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -88,335 +88,6 @@ function hasComponentProps(child: any): child is React.ReactElement { return !!child && !!child.props } -function handleDocumentScriptLoaderItems( - scriptLoader: { beforeInteractive?: any[] }, - __NEXT_DATA__: NEXT_DATA, - props: any -): void { - if (!props.children) return - - const scriptLoaderItems: ScriptProps[] = [] - - const children = Array.isArray(props.children) - ? props.children - : [props.children] - - const headChildren = children.find( - (child: React.ReactElement) => child.type === Head - )?.props?.children - const bodyChildren = children.find( - (child: React.ReactElement) => child.type === 'body' - )?.props?.children - - // Scripts with beforeInteractive can be placed inside Head or so children of both needs to be traversed - const combinedChildren = [ - ...(Array.isArray(headChildren) ? headChildren : [headChildren]), - ...(Array.isArray(bodyChildren) ? bodyChildren : [bodyChildren]), - ] - - React.Children.forEach(combinedChildren, (child: any) => { - if (!child) return - - if (child.type === Script) { - if (child.props.strategy === 'beforeInteractive') { - scriptLoader.beforeInteractive = ( - scriptLoader.beforeInteractive || [] - ).concat([ - { - ...child.props, - }, - ]) - return - } else if ( - ['lazyOnload', 'afterInteractive', 'worker'].includes( - child.props.strategy - ) - ) { - scriptLoaderItems.push(child.props) - return - } - } - }) - - __NEXT_DATA__.scriptLoader = scriptLoaderItems -} - -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 && ( -