wip(ssr): v-bind basic usage

This commit is contained in:
Evan You
2020-02-04 12:20:51 -05:00
parent 7f38c1e0ff
commit 6a5ed49ea9
25 changed files with 173 additions and 75 deletions

View File

@@ -0,0 +1,25 @@
import {
SourceLocation,
CompilerError,
createCompilerError,
DOMErrorCodes
} from '@vue/compiler-dom'
export interface SSRCompilerError extends CompilerError {
code: SSRErrorCodes
}
export function createSSRCompilerError(
code: SSRErrorCodes,
loc?: SourceLocation
): SSRCompilerError {
return createCompilerError(code, loc, SSRErrorMessages)
}
export const enum SSRErrorCodes {
X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM = DOMErrorCodes.__EXTEND_POINT__
}
export const SSRErrorMessages: { [code: number]: string } = {
[SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM]: `Custom directive is missing corresponding SSR transform and will be ignored.`
}

View File

@@ -7,20 +7,22 @@ import {
CompilerOptions,
transformExpression,
trackVForSlotScopes,
trackSlotScopes
trackSlotScopes,
noopDirectiveTransform
} from '@vue/compiler-dom'
import { ssrCodegenTransform } from './ssrCodegenTransform'
import { ssrTransformIf } from './transforms/ssrVIf'
import { ssrTransformFor } from './transforms/ssrVFor'
import { ssrTransformElement } from './transforms/ssrTransformElement'
import { ssrTransformComponent } from './transforms/ssrTransformComponent'
import { ssrTransformSlotOutlet } from './transforms/ssrTransformSlotOutlet'
export interface SSRCompilerOptions extends CompilerOptions {}
import { ssrTransformIf } from './transforms/ssrVIf'
import { ssrTransformFor } from './transforms/ssrVFor'
import { ssrVBind } from './transforms/ssrVBind'
import { ssrVModel } from './transforms/ssrVModel'
import { ssrVShow } from './transforms/ssrVShow'
export function compile(
template: string,
options: SSRCompilerOptions = {}
options: CompilerOptions = {}
): CodegenResult {
options = {
mode: 'cjs',
@@ -50,9 +52,13 @@ export function compile(
trackSlotScopes,
...(options.nodeTransforms || []) // user transforms
],
directiveTransforms: {
// TODO server-side directive transforms
...(options.directiveTransforms || {}) // user transforms
ssrDirectiveTransforms: {
on: noopDirectiveTransform,
cloak: noopDirectiveTransform,
bind: ssrVBind,
model: ssrVModel,
show: ssrVShow,
...(options.ssrDirectiveTransforms || {}) // user transforms
}
})

View File

@@ -4,9 +4,12 @@ import {
ElementTypes,
TemplateLiteral,
createTemplateLiteral,
createInterpolation
createInterpolation,
createCallExpression
} from '@vue/compiler-dom'
import { escapeHtml } from '@vue/shared'
import { createSSRCompilerError, SSRErrorCodes } from '../errors'
import { SSR_RENDER_ATTR } from '../runtimeHelpers'
export const ssrTransformElement: NodeTransform = (node, context) => {
if (
@@ -19,10 +22,25 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
let rawChildren
// v-bind="obj" or v-bind:[key] can potentially overwrite other static
// attrs and can affect final rendering result, so when they are present
// we need to bail out to full `renderAttrs`
const hasDynamicVBind = node.props.some(
p =>
p.type === NodeTypes.DIRECTIVE &&
p.name === 'bind' &&
(!p.arg || // v-bind="obj"
p.arg.type !== NodeTypes.SIMPLE_EXPRESSION || // v-bind:[_ctx.foo]
!p.arg.isStatic) // v-bind:[foo]
)
if (hasDynamicVBind) {
}
for (let i = 0; i < node.props.length; i++) {
const prop = node.props[i]
// special cases with children override
if (prop.type === NodeTypes.DIRECTIVE) {
// special cases with children override
if (prop.name === 'html' && prop.exp) {
node.children = []
rawChildren = prop.exp
@@ -40,13 +58,28 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
) {
node.children = [createInterpolation(prop.exp, prop.loc)]
// TODO handle <textrea> with dynamic v-bind
} else {
const directiveTransform = context.directiveTransforms[prop.name]
} else if (!hasDynamicVBind) {
// Directive transforms.
const directiveTransform = context.ssrDirectiveTransforms[prop.name]
if (directiveTransform) {
// TODO directive transforms
const { props } = directiveTransform(prop, node, context)
for (let j = 0; j < props.length; j++) {
const { key, value } = props[i]
openTag.push(
createCallExpression(context.helper(SSR_RENDER_ATTR), [
key,
value
])
)
}
} else {
// no corresponding ssr directive transform found.
// TODO emit error
context.onError(
createSSRCompilerError(
SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM,
prop.loc
)
)
}
}
} else {
@@ -54,7 +87,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
node.children = []
rawChildren = escapeHtml(prop.value.content)
} else {
} else if (!hasDynamicVBind) {
// static prop
openTag.push(
` ${prop.name}` +

View File

@@ -1 +1,18 @@
// TODO
import { DirectiveTransform, createObjectProperty } from '@vue/compiler-dom'
export const ssrVBind: DirectiveTransform = (dir, node, context) => {
if (!dir.exp) {
// error
return { props: [] }
} else {
// TODO modifiers
return {
props: [
createObjectProperty(
dir.arg!, // v-bind="obj" is handled separately
dir.exp
)
]
}
}
}

View File

@@ -1 +0,0 @@
// TODO

View File

@@ -1 +1,7 @@
// TODO
import { DirectiveTransform } from '@vue/compiler-dom'
export const ssrVModel: DirectiveTransform = (dir, node, context) => {
return {
props: []
}
}

View File

@@ -1 +0,0 @@
// TODO

View File

@@ -1 +1,7 @@
// TODO
import { DirectiveTransform } from '@vue/compiler-dom'
export const ssrVShow: DirectiveTransform = (dir, node, context) => {
return {
props: []
}
}