Repository: vuejs/vue-next
File: packages/compiler-vapor/src/transforms/transformElement.ts (TypeScript)

Code before:
      expressions,
      {
        type: IRNodeTypes.SET_DYNAMIC_PROPS,
        element: context.reference(),
        props: dynamicArgs,
        root: singleRoot,
      },
      getEffectIndex,
    )
  } else {
    let needsQuoting = false

    for (const prop of propsResult[1]) {
      const { key, values } = prop
      if (key.isStatic && values.length === 1 && values[0].isStatic) {
        const value = values[0].content

        if (!needsQuoting) template += ` `
        template += key.content

        if (value) {
          // https://html.spec.whatwg.org/multipage/introduction.html#intro-early-example
          needsQuoting = /[\s>]|^["'=]/.test(value)

          if (needsQuoting) {
            const encoded = value.replace(/"/g, '&#34;')

            template += `="${encoded}"`
          } else {
            template += `=${value}`
          }
        } else {
          needsQuoting = false
        }
      } else {
        dynamicProps.push(key.content)
        context.registerEffect(
          values,
          {
            type: IRNodeTypes.SET_PROP,
            element: context.reference(),
            prop,
            root: singleRoot,
            tag,
          },
          getEffectIndex,
        )
      }
    }
  }


Review comment (security):
This is a partial fix for `value='"foo"'` being turned into `value=""foo""`, I don't think this is the way to do it though, especially since this doesn't escape `&` as well.

I've looked into how Vue compiler deals with entities and it seems that we only ever do decoding (which makes sense for the original compiler, but it seems bad for Vapor where we generate HTML templates, we don't want static HTML code examples to be parsed as anything but text)

Code after:
      const ns = resolveSetupReference(tag.slice(0, dotIndex), context)
      if (ns) {
        tag = ns + tag.slice(dotIndex)
        asset = false
      }
    }

    if (asset) {
      // self referencing component (inferred from filename)
      if (context.selfName && capitalize(camelize(tag)) === context.selfName) {
        // generators/block.ts has special check for __self postfix when generating
        // component imports, which will pass additional `maybeSelfReference` flag
        // to `resolveComponent`.
        tag += `__self`
      }
      context.component.add(tag)
    }
  }

  context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
  context.dynamic.operation = {
    type: IRNodeTypes.CREATE_COMPONENT_NODE,
    id: context.reference(),
    tag,
    props: propsResult[0] ? propsResult[1] : [propsResult[1]],
    asset,
    root: singleRoot,
    slots: [...context.slots],
    once: context.inVOnce,
    dynamic: dynamicComponent,
    isCustomElement,
  }
  context.slots = []
}

function resolveDynamicComponent(node: ComponentNode) {
  const isProp = findProp(node, 'is', false, true /* allow empty */)
  if (!isProp) return

  if (isProp.type === NodeTypes.ATTRIBUTE) {
    return isProp.value && createSimpleExpression(isProp.value.content, true)
  } else {
    return (
      isProp.exp ||
      // #10469 handle :is shorthand
      extend(createSimpleExpression(`is`, false, isProp.arg!.loc), {
        ast: null,
      })
    )
  }
}