wip(compiler-ssr): v-html, v-text & textarea value

This commit is contained in:
Evan You 2020-02-03 11:46:14 -05:00
parent b59524e036
commit 090eb0ce67
6 changed files with 137 additions and 74 deletions

View File

@ -1,62 +0,0 @@
import { compile } from '../src'
function getString(src: string): string {
return compile(src).code.match(/_push\((.*)\)/)![1]
}
describe('element', () => {
test('basic elements', () => {
expect(getString(`<div></div>`)).toMatchInlineSnapshot(`"\`<div></div>\`"`)
expect(getString(`<div/>`)).toMatchInlineSnapshot(`"\`<div></div>\`"`)
})
test('static attrs', () => {
expect(getString(`<div id="foo" class="bar"></div>`)).toMatchInlineSnapshot(
`"\`<div id=\\"foo\\" class=\\"bar\\"></div>\`"`
)
})
test('nested elements', () => {
expect(
getString(`<div><span></span><span></span></div>`)
).toMatchInlineSnapshot(`"\`<div><span></span><span></span></div>\`"`)
})
test('void element', () => {
expect(getString(`<input>`)).toMatchInlineSnapshot(`"\`<input>\`"`)
})
})
describe('text', () => {
test('static text', () => {
expect(getString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
})
test('static text escape', () => {
expect(getString(`&lt;foo&gt;`)).toMatchInlineSnapshot(`"\`&lt;foo&gt;\`"`)
})
test('nested elements with static text', () => {
expect(
getString(`<div><span>hello</span><span>bye</span></div>`)
).toMatchInlineSnapshot(
`"\`<div><span>hello</span><span>bye</span></div>\`"`
)
})
test('interpolation', () => {
expect(getString(`foo {{ bar }} baz`)).toMatchInlineSnapshot(
`"\`foo \${interpolate(_ctx.bar)} baz\`"`
)
})
test('nested elements with interpolation', () => {
expect(
getString(
`<div><span>{{ foo }} bar</span><span>baz {{ qux }}</span></div>`
)
).toMatchInlineSnapshot(
`"\`<div><span>\${interpolate(_ctx.foo)} bar</span><span>baz \${interpolate(_ctx.qux)}</span></div>\`"`
)
})
})

View File

@ -0,0 +1,52 @@
import { getCompiledString } from './utils'
describe('element', () => {
test('basic elements', () => {
expect(getCompiledString(`<div></div>`)).toMatchInlineSnapshot(
`"\`<div></div>\`"`
)
expect(getCompiledString(`<div/>`)).toMatchInlineSnapshot(
`"\`<div></div>\`"`
)
})
test('static attrs', () => {
expect(
getCompiledString(`<div id="foo" class="bar"></div>`)
).toMatchInlineSnapshot(`"\`<div id=\\"foo\\" class=\\"bar\\"></div>\`"`)
})
test('nested elements', () => {
expect(
getCompiledString(`<div><span></span><span></span></div>`)
).toMatchInlineSnapshot(`"\`<div><span></span><span></span></div>\`"`)
})
test('void element', () => {
expect(getCompiledString(`<input>`)).toMatchInlineSnapshot(`"\`<input>\`"`)
})
test('v-html', () => {
expect(getCompiledString(`<div v-html="foo"/>`)).toMatchInlineSnapshot(
`"\`<div>\${_ctx.foo}</div>\`"`
)
})
test('v-text', () => {
expect(getCompiledString(`<div v-text="foo"/>`)).toMatchInlineSnapshot(
`"\`<div>\${interpolate(_ctx.foo)}</div>\`"`
)
})
test('<textarea> with dynamic value', () => {
expect(getCompiledString(`<textarea :value="foo"/>`)).toMatchInlineSnapshot(
`"\`<textarea>\${interpolate(_ctx.foo)}</textarea>\`"`
)
})
test('<textarea> with static value', () => {
expect(
getCompiledString(`<textarea value="fo&gt;o"/>`)
).toMatchInlineSnapshot(`"\`<textarea>fo&gt;o</textarea>\`"`)
})
})

View File

@ -0,0 +1,37 @@
import { getCompiledString } from './utils'
describe('text', () => {
test('static text', () => {
expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
})
test('static text escape', () => {
expect(getCompiledString(`&lt;foo&gt;`)).toMatchInlineSnapshot(
`"\`&lt;foo&gt;\`"`
)
})
test('nested elements with static text', () => {
expect(
getCompiledString(`<div><span>hello</span><span>bye</span></div>`)
).toMatchInlineSnapshot(
`"\`<div><span>hello</span><span>bye</span></div>\`"`
)
})
test('interpolation', () => {
expect(getCompiledString(`foo {{ bar }} baz`)).toMatchInlineSnapshot(
`"\`foo \${interpolate(_ctx.bar)} baz\`"`
)
})
test('nested elements with interpolation', () => {
expect(
getCompiledString(
`<div><span>{{ foo }} bar</span><span>baz {{ qux }}</span></div>`
)
).toMatchInlineSnapshot(
`"\`<div><span>\${interpolate(_ctx.foo)} bar</span><span>baz \${interpolate(_ctx.qux)}</span></div>\`"`
)
})
})

View File

@ -0,0 +1,5 @@
import { compile } from '../src'
export function getCompiledString(src: string): string {
return compile(src).code.match(/_push\((.*)\)/)![1]
}

View File

@ -1,4 +1,4 @@
import { registerRuntimeHelpers } from '@vue/compiler-core' import { registerRuntimeHelpers } from '@vue/compiler-dom'
export const INTERPOLATE = Symbol(`interpolate`) export const INTERPOLATE = Symbol(`interpolate`)

View File

@ -3,7 +3,8 @@ import {
NodeTypes, NodeTypes,
ElementTypes, ElementTypes,
TemplateLiteral, TemplateLiteral,
createTemplateLiteral createTemplateLiteral,
createInterpolation
} from '@vue/compiler-dom' } from '@vue/compiler-dom'
import { escapeHtml } from '@vue/shared' import { escapeHtml } from '@vue/shared'
@ -43,27 +44,57 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
// element // element
// generate the template literal representing the open tag. // generate the template literal representing the open tag.
const openTag: TemplateLiteral['elements'] = [`<${node.tag}`] const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
let rawChildren
for (let i = 0; i < node.props.length; i++) { for (let i = 0; i < node.props.length; i++) {
const prop = node.props[i] const prop = node.props[i]
if (prop.type === NodeTypes.DIRECTIVE) { if (prop.type === NodeTypes.DIRECTIVE) {
const directiveTransform = context.directiveTransforms[prop.name] // special cases with children override
if (directiveTransform) { if (prop.name === 'html' && prop.exp) {
// TODO directive transforms node.children = []
rawChildren = prop.exp
} else if (prop.name === 'text' && prop.exp) {
node.children = [createInterpolation(prop.exp, prop.loc)]
} else if (
// v-bind:value on textarea
node.tag === 'textarea' &&
prop.name === 'bind' &&
prop.exp &&
prop.arg &&
prop.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
prop.arg.isStatic &&
prop.arg.content === 'value'
) {
node.children = [createInterpolation(prop.exp, prop.loc)]
// TODO handle <textrea> with dynamic v-bind
} else { } else {
// no corresponding ssr directive transform found. const directiveTransform = context.directiveTransforms[prop.name]
// TODO emit error if (directiveTransform) {
// TODO directive transforms
} else {
// no corresponding ssr directive transform found.
// TODO emit error
}
} }
} else { } else {
// static prop // special case: value on <textarea>
openTag.push( if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
` ${prop.name}` + node.children = []
(prop.value ? `="${escapeHtml(prop.value.content)}"` : ``) rawChildren = escapeHtml(prop.value.content)
) } else {
// static prop
openTag.push(
` ${prop.name}` +
(prop.value ? `="${escapeHtml(prop.value.content)}"` : ``)
)
}
} }
} }
openTag.push(`>`) openTag.push(`>`)
if (rawChildren) {
openTag.push(rawChildren)
}
node.ssrCodegenNode = createTemplateLiteral(openTag) node.ssrCodegenNode = createTemplateLiteral(openTag)
} }
} }