]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-ssr): fix wrong attrs fallthrough on non-single-root v-if branches
authorEvan You <yyx990803@gmail.com>
Thu, 19 May 2022 03:21:17 +0000 (11:21 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 19 May 2022 03:21:17 +0000 (11:21 +0800)
fix #5140

packages/compiler-core/src/transform.ts
packages/compiler-ssr/__tests__/ssrComponent.spec.ts
packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts [new file with mode: 0644]
packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts

index 29a04bc77d568c41b268f6f9ad6717ebbeb85dc6..6397df92f3c6a3366bd3c5bbffce6f408c5e627e 100644 (file)
@@ -68,7 +68,7 @@ export interface DirectiveTransformResult {
   ssrTagParts?: TemplateLiteral['elements']
 }
 
-// A structural directive transform is a technically a NodeTransform;
+// A structural directive transform is technically also a NodeTransform;
 // Only v-if and v-for fall into this category.
 export type StructuralDirectiveTransform = (
   node: ElementNode,
index 24b926f83bc862861cb7b45fc63bfa4cf3b0c035..1a7961867391a0facc658c5fa70087ff82e5ebe4 100644 (file)
@@ -306,27 +306,6 @@ describe('ssr: components', () => {
               `)
       })
 
-      test('should inject attrs if root with coomments', () => {
-        expect(compile(`<!--root--><transition><div/></transition>`).code)
-          .toMatchInlineSnapshot(`
-                  "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
-
-                  return function ssrRender(_ctx, _push, _parent, _attrs) {
-                    _push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
-                  }"
-              `)
-      })
-
-      test('should not inject attrs if not root', () => {
-        expect(compile(`<div/><transition><div/></transition>`).code)
-          .toMatchInlineSnapshot(`
-                  "
-                  return function ssrRender(_ctx, _push, _parent, _attrs) {
-                    _push(\`<!--[--><div></div><div></div><!--]-->\`)
-                  }"
-              `)
-      })
-
       // #5352
       test('should push marker string if is slot root', () => {
         expect(
diff --git a/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts b/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts
new file mode 100644 (file)
index 0000000..6cbb0d3
--- /dev/null
@@ -0,0 +1,60 @@
+import { compile } from '../src'
+
+describe('ssr: attrs fallthrough', () => {
+  test('basic', () => {
+    expect(compile(`<div/>`).code).toMatchInlineSnapshot(`
+      "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+      }"
+    `)
+  })
+
+  test('with comments', () => {
+    expect(compile(`<!--!--><div/>`).code).toMatchInlineSnapshot(`
+      "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<!--[--><!--!--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
+      }"
+    `)
+  })
+
+  // #5140
+  test('should not inject to non-single-root if branches', () => {
+    expect(compile(`<div v-if="true"/><div/>`).code).toMatchInlineSnapshot(`
+      "
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<!--[-->\`)
+        if (true) {
+          _push(\`<div></div>\`)
+        } else {
+          _push(\`<!---->\`)
+        }
+        _push(\`<div></div><!--]-->\`)
+      }"
+    `)
+  })
+
+  test('fallthrough component content (root with coomments)', () => {
+    expect(compile(`<!--root--><transition><div/></transition>`).code)
+      .toMatchInlineSnapshot(`
+              "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+              return function ssrRender(_ctx, _push, _parent, _attrs) {
+                _push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
+              }"
+          `)
+  })
+
+  test('should not inject to fallthrough component content if not root', () => {
+    expect(compile(`<div/><transition><div/></transition>`).code)
+      .toMatchInlineSnapshot(`
+              "
+              return function ssrRender(_ctx, _push, _parent, _attrs) {
+                _push(\`<!--[--><div></div><div></div><!--]-->\`)
+              }"
+          `)
+  })
+})
index 79ab24eccd7aa5d8e26039bdd6759585640a35cd..2b5c350a557565c3820d587a9d635a2a0fc6c20f 100644 (file)
@@ -46,6 +46,25 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
   }
 
   if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) {
+    // detect cases where the parent v-if is not the only root level node
+    let hasEncounteredIf = false
+    for (const c of filterChild(parent)) {
+      if (
+        c.type === NodeTypes.IF ||
+        (c.type === NodeTypes.ELEMENT && findDir(c, 'if'))
+      ) {
+        // multiple root v-if
+        if (hasEncounteredIf) return
+        hasEncounteredIf = true
+      } else if (
+        // node before v-if
+        !hasEncounteredIf ||
+        // non else nodes
+        !(c.type === NodeTypes.ELEMENT && findDir(c, /else/, true))
+      ) {
+        return
+      }
+    }
     injectFallthroughAttrs(node.children[0])
   } else if (hasSingleChild(parent)) {
     injectFallthroughAttrs(node)