Просмотр исходного кода

chore: Merge branch 'minor' into edison/testVapor

daiwei 8 месяцев назад
Родитель
Сommit
28ba1cad6c

+ 6 - 0
packages/compiler-vapor/__tests__/transforms/transformText.spec.ts

@@ -48,4 +48,10 @@ describe('compiler: text transform', () => {
     expect(ir.block.operation).toMatchObject([])
     expect(ir.block.effect.length).toBe(1)
   })
+
+  it('escapes raw static text when generating the template string', () => {
+    const { ir } = compileWithTextTransform('<code>&lt;script&gt;</code>')
+    expect(ir.template).toContain('<code>&lt;script&gt;</code>')
+    expect(ir.template).not.toContain('<code><script></code>')
+  })
 })

+ 2 - 1
packages/compiler-vapor/src/transforms/transformComment.ts

@@ -6,6 +6,7 @@ import {
 } from '@vue/compiler-dom'
 import type { NodeTransform, TransformContext } from '../transform'
 import { DynamicFlag } from '../ir'
+import { escapeHtml } from '@vue/shared'
 
 export const transformComment: NodeTransform = (node, context) => {
   if (node.type !== NodeTypes.COMMENT) return
@@ -14,7 +15,7 @@ export const transformComment: NodeTransform = (node, context) => {
     context.comment.push(node)
     context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
   } else {
-    context.template += `<!--${node.content}-->`
+    context.template += `<!--${escapeHtml(node.content)}-->`
   }
 }
 

+ 16 - 5
packages/compiler-vapor/src/transforms/vFor.ts

@@ -2,6 +2,7 @@ import {
   type ElementNode,
   ElementTypes,
   ErrorCodes,
+  NodeTypes,
   type SimpleExpressionNode,
   createCompilerError,
 } from '@vue/compiler-dom'
@@ -28,7 +29,7 @@ export function processFor(
   node: ElementNode,
   dir: VaporDirectiveNode,
   context: TransformContext<ElementNode>,
-) {
+): (() => void) | undefined {
   if (!dir.exp) {
     context.options.onError(
       createCompilerError(ErrorCodes.X_V_FOR_NO_EXPRESSION, dir.loc),
@@ -50,10 +51,7 @@ export function processFor(
   const isComponent =
     node.tagType === ElementTypes.COMPONENT ||
     // template v-for with a single component child
-    (node.tag === 'template' &&
-      node.children.length === 1 &&
-      node.children[0].type === 1 &&
-      node.children[0].tagType === ElementTypes.COMPONENT)
+    isTemplateWithSingleComponent(node)
   context.node = node = wrapTemplate(node, ['for'])
   context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
   const id = context.reference()
@@ -93,3 +91,16 @@ export function processFor(
     }
   }
 }
+
+function isTemplateWithSingleComponent(node: ElementNode): boolean {
+  if (node.tag !== 'template') return false
+
+  const nonCommentChildren = node.children.filter(
+    c => c.type !== NodeTypes.COMMENT,
+  )
+  return (
+    nonCommentChildren.length === 1 &&
+    nonCommentChildren[0].type === NodeTypes.ELEMENT &&
+    nonCommentChildren[0].tagType === ElementTypes.COMPONENT
+  )
+}

+ 4 - 0
packages/runtime-dom/src/index.ts

@@ -369,3 +369,7 @@ export {
   handleMovedChildren,
   baseApplyTranslation,
 } from './components/TransitionGroup'
+/**
+ * @internal
+ */
+export { unsafeToTrustedHTML } from './nodeOps'

+ 118 - 0
packages/runtime-vapor/__tests__/hmr.spec.ts

@@ -0,0 +1,118 @@
+// TODO: port tests from packages/runtime-core/__tests__/hmr.spec.ts
+
+import { type HMRRuntime, ref } from '@vue/runtime-dom'
+import { makeRender } from './_utils'
+import {
+  child,
+  createComponent,
+  renderEffect,
+  setText,
+  template,
+} from '@vue/runtime-vapor'
+
+declare var __VUE_HMR_RUNTIME__: HMRRuntime
+const { createRecord, reload } = __VUE_HMR_RUNTIME__
+
+const define = makeRender()
+
+describe('hot module replacement', () => {
+  test('child reload + parent reload', async () => {
+    const root = document.createElement('div')
+    const childId = 'test1-child-reload'
+    const parentId = 'test1-parent-reload'
+
+    const { component: Child } = define({
+      __hmrId: childId,
+      setup() {
+        const msg = ref('child')
+        return { msg }
+      },
+      render(ctx) {
+        const n0 = template(`<div> </div>`)()
+        const x0 = child(n0 as any)
+        renderEffect(() => setText(x0 as any, ctx.msg))
+        return [n0]
+      },
+    })
+    createRecord(childId, Child as any)
+
+    const { mount, component: Parent } = define({
+      __hmrId: parentId,
+      setup() {
+        const msg = ref('root')
+        return { msg }
+      },
+      render(ctx) {
+        const n0 = createComponent(Child)
+        const n1 = template(`<div> </div>`)()
+        const x0 = child(n1 as any)
+        renderEffect(() => setText(x0 as any, ctx.msg))
+        return [n0, n1]
+      },
+    }).create()
+    createRecord(parentId, Parent as any)
+    mount(root)
+
+    expect(root.innerHTML).toMatchInlineSnapshot(
+      `"<div>child</div><div>root</div>"`,
+    )
+
+    // reload child
+    reload(childId, {
+      __hmrId: childId,
+      __vapor: true,
+      setup() {
+        const msg = ref('child changed')
+        return { msg }
+      },
+      render(ctx: any) {
+        const n0 = template(`<div> </div>`)()
+        const x0 = child(n0 as any)
+        renderEffect(() => setText(x0 as any, ctx.msg))
+        return [n0]
+      },
+    })
+    expect(root.innerHTML).toMatchInlineSnapshot(
+      `"<div>child changed</div><div>root</div>"`,
+    )
+
+    // reload child again
+    reload(childId, {
+      __hmrId: childId,
+      __vapor: true,
+      setup() {
+        const msg = ref('child changed2')
+        return { msg }
+      },
+      render(ctx: any) {
+        const n0 = template(`<div> </div>`)()
+        const x0 = child(n0 as any)
+        renderEffect(() => setText(x0 as any, ctx.msg))
+        return [n0]
+      },
+    })
+    expect(root.innerHTML).toMatchInlineSnapshot(
+      `"<div>child changed2</div><div>root</div>"`,
+    )
+
+    // reload parent
+    reload(parentId, {
+      __hmrId: parentId,
+      __vapor: true,
+      setup() {
+        const msg = ref('root changed')
+        return { msg }
+      },
+      render(ctx: any) {
+        const n0 = createComponent(Child)
+        const n1 = template(`<div> </div>`)()
+        const x0 = child(n1 as any)
+        renderEffect(() => setText(x0 as any, ctx.msg))
+        return [n0, n1]
+      },
+    })
+    expect(root.innerHTML).toMatchInlineSnapshot(
+      `"<div>child changed2</div><div>root changed</div>"`,
+    )
+  })
+})

+ 2 - 2
packages/runtime-vapor/src/dom/prop.ts

@@ -24,6 +24,7 @@ import {
   shouldSetAsProp,
   toClassSet,
   toStyleMap,
+  unsafeToTrustedHTML,
   vShowHidden,
   warn,
   warnPropMismatch,
@@ -327,8 +328,7 @@ export function setElementText(
 }
 
 export function setHtml(el: TargetElement, value: any): void {
-  value = value == null ? '' : value
-
+  value = value == null ? '' : unsafeToTrustedHTML(value)
   if (el.$html !== value) {
     el.innerHTML = el.$html = value
   }