Repository: nuxt/nuxt
File: packages/nuxt/src/components/plugins/lazy-hydration-transform.ts (TypeScript)

Code before:
import MagicString from 'magic-string'
import { camelCase, pascalCase } from 'scule'

import { parse, walk } from 'ultrahtml'
import { isVue } from '../../core/utils'
import { logger } from '../../utils'
import type { Component, ComponentsOptions } from 'nuxt/schema'

interface LoaderOptions {
  getComponents (): Component[]
  sourcemap?: boolean
  transform?: ComponentsOptions['transform']
}

const TEMPLATE_RE = /<template>([\s\S]*)<\/template>/
const hydrationStrategyMap = {
  hydrateOnIdle: 'Idle',
  hydrateOnVisible: 'Visible',
  hydrateOnInteraction: 'Interaction',
  hydrateOnMediaQuery: 'MediaQuery',
  hydrateAfter: 'Time',
  hydrateWhen: 'If',
  hydrateNever: 'Never',
}

const LAZY_HYDRATION_PROPS_RE = /\b(?:hydrate-on-idle|HydrateOnIdle|hydrate-on-visible|HydrateOnVisible|hydrate-on-interaction|HydrateOnInteraction|hydrate-on-media-query|HydrateOnMediaQuery|hydrate-after|HydrateAfter|hydrate-when|HydrateWhen|hydrate-never|HydrateNever)\b/

export const LazyHydrationTransformPlugin = (options: LoaderOptions) => createUnplugin(() => {
  const exclude = options.transform?.exclude || []
  const include = options.transform?.include || []

  return {
    name: 'nuxt:components-loader-pre',
    enforce: 'pre',
    transformInclude (id) {
      if (exclude.some(pattern => pattern.test(id))) {
        return false
      }
      if (include.some(pattern => pattern.test(id))) {
        return true
      }

      return isVue(id)
    },
    transform: {
      filter: {
        code: { include: TEMPLATE_RE },
      },
      async handler (code) {
        // change <LazyMyComponent hydrate-on-idle /> to <LazyIdleMyComponent hydrate-on-idle />
        const { 0: template, index: offset = 0 } = code.match(TEMPLATE_RE) || {}

Review comment (security):
[nitpick] The regex is quite repetitive and could become hard to maintain as strategies evolve. Consider consolidating patterns and using a case-insensitive flag with grouped alternations, for example: `const LAZY_HYDRATION_PROPS_RE = /\b(?:hydrate(?:-on-(?:idle|visible|interaction|media-query)|-after|-when|-never)|Hydrate(?:On(?:Idle|Visible|Interaction|MediaQuery)|After|When|Never))\b/;`
```suggestion
const LAZY_HYDRATION_PROPS_RE = new RegExp(
  `\\b(?:${Object.keys(hydrationStrategyMap)
    .map(key => key.replace(/([A-Z])/g, '-$1').toLowerCase() + '|' + key)
    .join('|')})\\b`,
  'i'
)
```

Code after:
import MagicString from 'magic-string'
import { camelCase, pascalCase } from 'scule'

import { parse, walk } from 'ultrahtml'
import { isVue } from '../../core/utils'
import { logger } from '../../utils'
import type { Component, ComponentsOptions } from 'nuxt/schema'

interface LoaderOptions {
  getComponents (): Component[]
  sourcemap?: boolean
  transform?: ComponentsOptions['transform']
}

const TEMPLATE_RE = /<template>([\s\S]*)<\/template>/
const hydrationStrategyMap = {
  hydrateOnIdle: 'Idle',
  hydrateOnVisible: 'Visible',
  hydrateOnInteraction: 'Interaction',
  hydrateOnMediaQuery: 'MediaQuery',
  hydrateAfter: 'Time',
  hydrateWhen: 'If',
  hydrateNever: 'Never',
}

const LAZY_HYDRATION_PROPS_RE = /\b(?:hydrate-on-idle|hydrateOnIdle|hydrate-on-visible|hydrateOnVisible|hydrate-on-interaction|hydrateOnInteraction|hydrate-on-media-query|hydrateOnMediaQuery|hydrate-after|hydrateAfter|hydrate-when|hydrateWhen|hydrate-never|hydrateNever)\b/

export const LazyHydrationTransformPlugin = (options: LoaderOptions) => createUnplugin(() => {
  const exclude = options.transform?.exclude || []
  const include = options.transform?.include || []

  return {
    name: 'nuxt:components-loader-pre',
    enforce: 'pre',
    transformInclude (id) {
      if (exclude.some(pattern => pattern.test(id))) {
        return false
      }
      if (include.some(pattern => pattern.test(id))) {
        return true
      }

      return isVue(id)
    },
    transform: {
      filter: {
        code: { include: TEMPLATE_RE },
      },
      async handler (code) {
        // change <LazyMyComponent hydrate-on-idle /> to <LazyIdleMyComponent hydrate-on-idle />
        const { 0: template, index: offset = 0 } = code.match(TEMPLATE_RE) || {}