Repository: nuxt/nuxt
File: packages/nuxt/src/app/composables/router.ts (TypeScript)

Code before:
/**
 * This will abort navigation within a Nuxt route middleware handler.
 * @since 3.0.0
 */
export const abortNavigation = (err?: string | Partial<NuxtError>) => {
  if (import.meta.dev && !isProcessingMiddleware()) {
    throw new Error('abortNavigation() is only usable inside a route middleware handler.')
  }

  if (!err) { return false }

  err = createError(err)

  if (err.fatal) {
    useNuxtApp().runWithContext(() => showError(err as NuxtError))
  }

  throw err
}

/**
 * Sets the layout for the current page.
 * TODO type props
 * @since 3.0.0
 */
export const setPageLayout = <Layout extends keyof NuxtLayouts>(layout: unknown extends Layout ? string : Layout, props?: typeof layout extends Layout ? NuxtLayouts[Layout] : never) => {
  const nuxtApp = useNuxtApp()
  if (import.meta.server) {
    if (import.meta.dev && getCurrentInstance() && nuxtApp.payload.state._layout !== layout) {
      console.warn('[warn] [nuxt] `setPageLayout` should not be called to change the layout on the server within a component as this will cause hydration errors.')
    }
    nuxtApp.payload.state._layout = layout
    nuxtApp.payload.state._layoutProps = props
  }
  if (import.meta.dev && nuxtApp.isHydrating && nuxtApp.payload.serverRendered && nuxtApp.payload.state._layout !== layout) {
    console.warn('[warn] [nuxt] `setPageLayout` should not be called to change the layout during hydration as this will cause hydration errors.')
  }
  const inMiddleware = isProcessingMiddleware()
  if (inMiddleware || import.meta.server || nuxtApp.isHydrating) {
    const unsubscribe = useRouter().beforeResolve((to) => {
      to.meta.layout = layout as Exclude<PageMeta['layout'], Ref | false>
      to.meta.layoutProps = props
      unsubscribe()
    })
  }
  if (!inMiddleware) {
    const route = useRoute()
    route.meta.layout = layout as Exclude<PageMeta['layout'], Ref | false>
    route.meta.layoutProps = props
  }
}

Review comment (suggestion):
we probably do have to constrain the types to ensure they are serialisable (e.g. can't contain functions.....)

Code after:

/**
 * This will abort navigation within a Nuxt route middleware handler.
 * @since 3.0.0
 */
export const abortNavigation = (err?: string | Partial<NuxtError>) => {
  if (import.meta.dev && !isProcessingMiddleware()) {
    throw new Error('abortNavigation() is only usable inside a route middleware handler.')
  }

  if (!err) { return false }

  err = createError(err)

  if (err.fatal) {
    useNuxtApp().runWithContext(() => showError(err as NuxtError))
  }

  throw err
}

/**
 * Sets the layout for the current page.
 * @since 3.0.0
 */
export const setPageLayout = <Layout extends keyof NuxtLayouts>(layout: unknown extends Layout ? string : Layout, props?: typeof layout extends Layout ? MakeSerializableObject<NuxtLayouts[Layout]> : never) => {
  const nuxtApp = useNuxtApp()
  if (import.meta.server) {
    if (import.meta.dev && getCurrentInstance() && nuxtApp.payload.state._layout !== layout) {
      console.warn('[warn] [nuxt] `setPageLayout` should not be called to change the layout on the server within a component as this will cause hydration errors.')
    }
    nuxtApp.payload.state._layout = layout
    nuxtApp.payload.state._layoutProps = props
  }
  if (import.meta.dev && nuxtApp.isHydrating && nuxtApp.payload.serverRendered && nuxtApp.payload.state._layout !== layout) {
    console.warn('[warn] [nuxt] `setPageLayout` should not be called to change the layout during hydration as this will cause hydration errors.')
  }
  const inMiddleware = isProcessingMiddleware()
  if (inMiddleware || import.meta.server || nuxtApp.isHydrating) {
    const unsubscribe = useRouter().beforeResolve((to) => {
      to.meta.layout = layout as Exclude<PageMeta['layout'], Ref | false>
      to.meta.layoutProps = props
      unsubscribe()
    })
  }
  if (!inMiddleware) {
    const route = useRoute()
    route.meta.layout = layout as Exclude<PageMeta['layout'], Ref | false>
    route.meta.layoutProps = props
  }
}