]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-ssr): handle comments codegen + refactor ssr codegen transform
authorEvan You <yyx990803@gmail.com>
Fri, 1 May 2020 21:04:36 +0000 (17:04 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 1 May 2020 21:04:36 +0000 (17:04 -0400)
packages/compiler-ssr/__tests__/ssrText.spec.ts
packages/compiler-ssr/src/errors.ts
packages/compiler-ssr/src/ssrCodegenTransform.ts
packages/server-renderer/src/renderToString.ts
packages/shared/src/escapeHtml.ts

index 21ef5518cbf8dbd273ae3d80dcf1e3959bb2381a..281f518f07f2978a6388f0447959b1ff0aa4838f 100644 (file)
@@ -6,6 +6,12 @@ describe('ssr: text', () => {
     expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
   })
 
+  test('comments', () => {
+    expect(getCompiledString(`<!--bar-->`)).toMatchInlineSnapshot(
+      `"\`<!--bar-->\`"`
+    )
+  })
+
   test('static text escape', () => {
     expect(getCompiledString(`&lt;foo&gt;`)).toMatchInlineSnapshot(
       `"\`&lt;foo&gt;\`"`
index 282c918f92629a9a728c5acc38189b3d155c8f06..d3e209add1293498f403aad2a495bd0a464eb2bf 100644 (file)
@@ -19,11 +19,13 @@ export function createSSRCompilerError(
 export const enum SSRErrorCodes {
   X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM = DOMErrorCodes.__EXTEND_POINT__,
   X_SSR_UNSAFE_ATTR_NAME,
-  X_SSR_NO_TELEPORT_TARGET
+  X_SSR_NO_TELEPORT_TARGET,
+  X_SSR_INVALID_AST_NODE
 }
 
 export const SSRErrorMessages: { [code: number]: string } = {
   [SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM]: `Custom directive is missing corresponding SSR transform and will be ignored.`,
   [SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: `Unsafe attribute name for SSR.`,
-  [SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `No target prop on teleport element.`
+  [SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `No target prop on teleport element.`,
+  [SSRErrorCodes.X_SSR_INVALID_AST_NODE]: `Invalid AST node during ssr transform`
 }
index 2d68800f1d8e88190ae58cdcf9b09d50e4bfe407..cca7318e79ed9dbe9d80a7dbd4bd99f32955b477 100644 (file)
@@ -20,6 +20,7 @@ import { ssrProcessFor } from './transforms/ssrVFor'
 import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'
 import { ssrProcessComponent } from './transforms/ssrTransformComponent'
 import { ssrProcessElement } from './transforms/ssrTransformElement'
+import { createSSRCompilerError, SSRErrorCodes } from './errors'
 
 // Because SSR codegen output is completely different from client-side output
 // (e.g. multiple elements can be concatenated into a single template literal
@@ -115,24 +116,70 @@ export function processChildren(
   }
   for (let i = 0; i < children.length; i++) {
     const child = children[i]
-    if (child.type === NodeTypes.ELEMENT) {
-      if (child.tagType === ElementTypes.ELEMENT) {
-        ssrProcessElement(child, context)
-      } else if (child.tagType === ElementTypes.COMPONENT) {
-        ssrProcessComponent(child, context)
-      } else if (child.tagType === ElementTypes.SLOT) {
-        ssrProcessSlotOutlet(child, context)
-      }
-    } else if (child.type === NodeTypes.TEXT) {
-      context.pushStringPart(escapeHtml(child.content))
-    } else if (child.type === NodeTypes.INTERPOLATION) {
-      context.pushStringPart(
-        createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
-      )
-    } else if (child.type === NodeTypes.IF) {
-      ssrProcessIf(child, context)
-    } else if (child.type === NodeTypes.FOR) {
-      ssrProcessFor(child, context)
+    switch (child.type) {
+      case NodeTypes.ELEMENT:
+        switch (child.tagType) {
+          case ElementTypes.ELEMENT:
+            ssrProcessElement(child, context)
+            break
+          case ElementTypes.COMPONENT:
+            ssrProcessComponent(child, context)
+            break
+          case ElementTypes.SLOT:
+            ssrProcessSlotOutlet(child, context)
+            break
+          case ElementTypes.TEMPLATE:
+            // TODO
+            break
+          default:
+            context.onError(
+              createSSRCompilerError(
+                SSRErrorCodes.X_SSR_INVALID_AST_NODE,
+                (child as any).loc
+              )
+            )
+            // make sure we exhaust all possible types
+            const exhaustiveCheck: never = child
+            return exhaustiveCheck
+        }
+        break
+      case NodeTypes.TEXT:
+        context.pushStringPart(escapeHtml(child.content))
+        break
+      case NodeTypes.COMMENT:
+        // no need to escape comment here because the AST can only
+        // contain valid comments.
+        context.pushStringPart(`<!--${child.content}-->`)
+        break
+      case NodeTypes.INTERPOLATION:
+        context.pushStringPart(
+          createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
+        )
+        break
+      case NodeTypes.IF:
+        ssrProcessIf(child, context)
+        break
+      case NodeTypes.FOR:
+        ssrProcessFor(child, context)
+        break
+      case NodeTypes.IF_BRANCH:
+        // no-op - handled by ssrProcessIf
+        break
+      case NodeTypes.TEXT_CALL:
+      case NodeTypes.COMPOUND_EXPRESSION:
+        // no-op - these two types can never appear as template child node since
+        // `transformText` is not used during SSR compile.
+        break
+      default:
+        context.onError(
+          createSSRCompilerError(
+            SSRErrorCodes.X_SSR_INVALID_AST_NODE,
+            (child as any).loc
+          )
+        )
+        // make sure we exhaust all possible types
+        const exhaustiveCheck: never = child
+        return exhaustiveCheck
     }
   }
   if (asFragment) {
index 57f4ab1d32cd0f6b788f1916eb6d78cc222ab129..8720929b0e1b8436a91671b25ee91a86d9ae8cc3 100644 (file)
@@ -27,7 +27,8 @@ import {
   isVoidTag,
   escapeHtml,
   NO,
-  generateCodeFrame
+  generateCodeFrame,
+  escapeHtmlComment
 } from '@vue/shared'
 import { compile } from '@vue/compiler-ssr'
 import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
@@ -230,9 +231,6 @@ function ssrCompile(
   return (compileCache[template] = Function('require', code)(require))
 }
 
-// https://www.w3.org/TR/html52/syntax.html#comments
-const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g
-
 function renderVNode(
   push: PushFn,
   vnode: VNode,
@@ -245,9 +243,7 @@ function renderVNode(
       break
     case Comment:
       push(
-        children
-          ? `<!--${(children as string).replace(commentStripRE, '')}-->`
-          : `<!---->`
+        children ? `<!--${escapeHtmlComment(children as string)}-->` : `<!---->`
       )
       break
     case Static:
index 7546471a9cb09ffb12a7e8a689d68ef33478fb93..4a6435e6a517cd1b0b0d9be9a1adcfdee80a2b40 100644 (file)
@@ -43,3 +43,10 @@ export function escapeHtml(string: unknown) {
 
   return lastIndex !== index ? html + str.substring(lastIndex, index) : html
 }
+
+// https://www.w3.org/TR/html52/syntax.html#comments
+const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g
+
+export function escapeHtmlComment(src: string): string {
+  return src.replace(commentStripRE, '')
+}