Skip to content

Commit 6c60ce1

Browse files
committed
fix(compiler-ssr): handle comments codegen + refactor ssr codegen transform
1 parent 0e730c0 commit 6c60ce1

File tree

5 files changed

+85
-27
lines changed

5 files changed

+85
-27
lines changed

packages/compiler-ssr/__tests__/ssrText.spec.ts

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ describe('ssr: text', () => {
66
expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
77
})
88

9+
test('comments', () => {
10+
expect(getCompiledString(`<!--bar-->`)).toMatchInlineSnapshot(
11+
`"\`<!--bar-->\`"`
12+
)
13+
})
14+
915
test('static text escape', () => {
1016
expect(getCompiledString(`&lt;foo&gt;`)).toMatchInlineSnapshot(
1117
`"\`&lt;foo&gt;\`"`

packages/compiler-ssr/src/errors.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ export function createSSRCompilerError(
1919
export const enum SSRErrorCodes {
2020
X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM = DOMErrorCodes.__EXTEND_POINT__,
2121
X_SSR_UNSAFE_ATTR_NAME,
22-
X_SSR_NO_TELEPORT_TARGET
22+
X_SSR_NO_TELEPORT_TARGET,
23+
X_SSR_INVALID_AST_NODE
2324
}
2425

2526
export const SSRErrorMessages: { [code: number]: string } = {
2627
[SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM]: `Custom directive is missing corresponding SSR transform and will be ignored.`,
2728
[SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: `Unsafe attribute name for SSR.`,
28-
[SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `No target prop on teleport element.`
29+
[SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `No target prop on teleport element.`,
30+
[SSRErrorCodes.X_SSR_INVALID_AST_NODE]: `Invalid AST node during ssr transform`
2931
}

packages/compiler-ssr/src/ssrCodegenTransform.ts

+65-18
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ssrProcessFor } from './transforms/ssrVFor'
2020
import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'
2121
import { ssrProcessComponent } from './transforms/ssrTransformComponent'
2222
import { ssrProcessElement } from './transforms/ssrTransformElement'
23+
import { createSSRCompilerError, SSRErrorCodes } from './errors'
2324

2425
// Because SSR codegen output is completely different from client-side output
2526
// (e.g. multiple elements can be concatenated into a single template literal
@@ -115,24 +116,70 @@ export function processChildren(
115116
}
116117
for (let i = 0; i < children.length; i++) {
117118
const child = children[i]
118-
if (child.type === NodeTypes.ELEMENT) {
119-
if (child.tagType === ElementTypes.ELEMENT) {
120-
ssrProcessElement(child, context)
121-
} else if (child.tagType === ElementTypes.COMPONENT) {
122-
ssrProcessComponent(child, context)
123-
} else if (child.tagType === ElementTypes.SLOT) {
124-
ssrProcessSlotOutlet(child, context)
125-
}
126-
} else if (child.type === NodeTypes.TEXT) {
127-
context.pushStringPart(escapeHtml(child.content))
128-
} else if (child.type === NodeTypes.INTERPOLATION) {
129-
context.pushStringPart(
130-
createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
131-
)
132-
} else if (child.type === NodeTypes.IF) {
133-
ssrProcessIf(child, context)
134-
} else if (child.type === NodeTypes.FOR) {
135-
ssrProcessFor(child, context)
119+
switch (child.type) {
120+
case NodeTypes.ELEMENT:
121+
switch (child.tagType) {
122+
case ElementTypes.ELEMENT:
123+
ssrProcessElement(child, context)
124+
break
125+
case ElementTypes.COMPONENT:
126+
ssrProcessComponent(child, context)
127+
break
128+
case ElementTypes.SLOT:
129+
ssrProcessSlotOutlet(child, context)
130+
break
131+
case ElementTypes.TEMPLATE:
132+
// TODO
133+
break
134+
default:
135+
context.onError(
136+
createSSRCompilerError(
137+
SSRErrorCodes.X_SSR_INVALID_AST_NODE,
138+
(child as any).loc
139+
)
140+
)
141+
// make sure we exhaust all possible types
142+
const exhaustiveCheck: never = child
143+
return exhaustiveCheck
144+
}
145+
break
146+
case NodeTypes.TEXT:
147+
context.pushStringPart(escapeHtml(child.content))
148+
break
149+
case NodeTypes.COMMENT:
150+
// no need to escape comment here because the AST can only
151+
// contain valid comments.
152+
context.pushStringPart(`<!--${child.content}-->`)
153+
break
154+
case NodeTypes.INTERPOLATION:
155+
context.pushStringPart(
156+
createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
157+
)
158+
break
159+
case NodeTypes.IF:
160+
ssrProcessIf(child, context)
161+
break
162+
case NodeTypes.FOR:
163+
ssrProcessFor(child, context)
164+
break
165+
case NodeTypes.IF_BRANCH:
166+
// no-op - handled by ssrProcessIf
167+
break
168+
case NodeTypes.TEXT_CALL:
169+
case NodeTypes.COMPOUND_EXPRESSION:
170+
// no-op - these two types can never appear as template child node since
171+
// `transformText` is not used during SSR compile.
172+
break
173+
default:
174+
context.onError(
175+
createSSRCompilerError(
176+
SSRErrorCodes.X_SSR_INVALID_AST_NODE,
177+
(child as any).loc
178+
)
179+
)
180+
// make sure we exhaust all possible types
181+
const exhaustiveCheck: never = child
182+
return exhaustiveCheck
136183
}
137184
}
138185
if (asFragment) {

packages/server-renderer/src/renderToString.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
isVoidTag,
2828
escapeHtml,
2929
NO,
30-
generateCodeFrame
30+
generateCodeFrame,
31+
escapeHtmlComment
3132
} from '@vue/shared'
3233
import { compile } from '@vue/compiler-ssr'
3334
import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
@@ -230,9 +231,6 @@ function ssrCompile(
230231
return (compileCache[template] = Function('require', code)(require))
231232
}
232233

233-
// https://www.w3.org/TR/html52/syntax.html#comments
234-
const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g
235-
236234
function renderVNode(
237235
push: PushFn,
238236
vnode: VNode,
@@ -245,9 +243,7 @@ function renderVNode(
245243
break
246244
case Comment:
247245
push(
248-
children
249-
? `<!--${(children as string).replace(commentStripRE, '')}-->`
250-
: `<!---->`
246+
children ? `<!--${escapeHtmlComment(children as string)}-->` : `<!---->`
251247
)
252248
break
253249
case Static:

packages/shared/src/escapeHtml.ts

+7
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,10 @@ export function escapeHtml(string: unknown) {
4343

4444
return lastIndex !== index ? html + str.substring(lastIndex, index) : html
4545
}
46+
47+
// https://www.w3.org/TR/html52/syntax.html#comments
48+
const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g
49+
50+
export function escapeHtmlComment(src: string): string {
51+
return src.replace(commentStripRE, '')
52+
}

0 commit comments

Comments
 (0)