wip(ssr): ssr helper codegen
This commit is contained in:
@@ -34,13 +34,13 @@ describe('ssr: element', () => {
|
||||
|
||||
test('v-text', () => {
|
||||
expect(getCompiledString(`<div v-text="foo"/>`)).toMatchInlineSnapshot(
|
||||
`"\`<div>\${interpolate(_ctx.foo)}</div>\`"`
|
||||
`"\`<div>\${_interpolate(_ctx.foo)}</div>\`"`
|
||||
)
|
||||
})
|
||||
|
||||
test('<textarea> with dynamic value', () => {
|
||||
expect(getCompiledString(`<textarea :value="foo"/>`)).toMatchInlineSnapshot(
|
||||
`"\`<textarea>\${interpolate(_ctx.foo)}</textarea>\`"`
|
||||
`"\`<textarea>\${_interpolate(_ctx.foo)}</textarea>\`"`
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { compile } from '../src'
|
||||
import { getCompiledString } from './utils'
|
||||
|
||||
describe('ssr: text', () => {
|
||||
@@ -20,18 +21,25 @@ describe('ssr: text', () => {
|
||||
})
|
||||
|
||||
test('interpolation', () => {
|
||||
expect(getCompiledString(`foo {{ bar }} baz`)).toMatchInlineSnapshot(
|
||||
`"\`foo \${interpolate(_ctx.bar)} baz\`"`
|
||||
)
|
||||
expect(compile(`foo {{ bar }} baz`).code).toMatchInlineSnapshot(`
|
||||
"const { _interpolate } = require(\\"@vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
_push(\`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>\`"`
|
||||
)
|
||||
compile(`<div><span>{{ foo }} bar</span><span>baz {{ qux }}</span></div>`)
|
||||
.code
|
||||
).toMatchInlineSnapshot(`
|
||||
"const { _interpolate } = require(\\"@vue/server-renderer\\")
|
||||
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
_push(\`<div><span>\${_interpolate(_ctx.foo)} bar</span><span>baz \${_interpolate(_ctx.qux)}</span></div>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,20 +22,23 @@ export function compile(
|
||||
template: string,
|
||||
options: SSRCompilerOptions = {}
|
||||
): CodegenResult {
|
||||
// apply DOM-specific parsing options
|
||||
options = {
|
||||
mode: 'cjs',
|
||||
...options,
|
||||
// apply DOM-specific parsing options
|
||||
...parserOptions,
|
||||
...options
|
||||
ssr: true,
|
||||
// always prefix since compiler-ssr doesn't have size concern
|
||||
prefixIdentifiers: true,
|
||||
// disalbe optimizations that are unnecessary for ssr
|
||||
cacheHandlers: false,
|
||||
hoistStatic: false
|
||||
}
|
||||
|
||||
const ast = baseParse(template, options)
|
||||
|
||||
transform(ast, {
|
||||
...options,
|
||||
prefixIdentifiers: true,
|
||||
// disalbe optimizations that are unnecessary for ssr
|
||||
cacheHandlers: false,
|
||||
hoistStatic: false,
|
||||
nodeTransforms: [
|
||||
ssrTransformIf,
|
||||
ssrTransformFor,
|
||||
@@ -57,10 +60,5 @@ export function compile(
|
||||
// by replacing ast.codegenNode.
|
||||
ssrCodegenTransform(ast, options)
|
||||
|
||||
return generate(ast, {
|
||||
mode: 'cjs',
|
||||
...options,
|
||||
ssr: true,
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
return generate(ast, options)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import { registerRuntimeHelpers } from '@vue/compiler-dom'
|
||||
|
||||
export const INTERPOLATE = Symbol(`interpolate`)
|
||||
export const SSR_INTERPOLATE = Symbol(`interpolate`)
|
||||
export const SSR_RENDER_COMPONENT = Symbol(`renderComponent`)
|
||||
export const SSR_RENDER_SLOT = Symbol(`renderSlot`)
|
||||
export const SSR_RENDER_CLASS = Symbol(`renderClass`)
|
||||
export const SSR_RENDER_STYLE = Symbol(`renderStyle`)
|
||||
export const SSR_RENDER_PROPS = Symbol(`renderProps`)
|
||||
export const SSR_RENDER_LIST = Symbol(`renderList`)
|
||||
|
||||
// Note: these are helpers imported from @vue/server-renderer
|
||||
// make sure the names match!
|
||||
registerRuntimeHelpers({
|
||||
[INTERPOLATE]: `interpolate`
|
||||
[SSR_INTERPOLATE]: `_interpolate`,
|
||||
[SSR_RENDER_COMPONENT]: `_renderComponent`,
|
||||
[SSR_RENDER_SLOT]: `_renderSlot`,
|
||||
[SSR_RENDER_CLASS]: `_renderClass`,
|
||||
[SSR_RENDER_STYLE]: `_renderStyle`,
|
||||
[SSR_RENDER_PROPS]: `_renderProps`,
|
||||
[SSR_RENDER_LIST]: `_renderList`
|
||||
})
|
||||
|
||||
@@ -14,8 +14,9 @@ import {
|
||||
CallExpression
|
||||
} from '@vue/compiler-dom'
|
||||
import { isString, escapeHtml, NO } from '@vue/shared'
|
||||
import { INTERPOLATE } from './runtimeHelpers'
|
||||
import { SSR_INTERPOLATE } from './runtimeHelpers'
|
||||
import { processIf } from './transforms/ssrVIf'
|
||||
import { processFor } from './transforms/ssrVFor'
|
||||
|
||||
// Because SSR codegen output is completely different from client-side output
|
||||
// (e.g. multiple elements can be concatenated into a single template literal
|
||||
@@ -37,17 +38,26 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
||||
}
|
||||
|
||||
ast.codegenNode = createBlockStatement(context.body)
|
||||
ast.ssrHelpers = [...context.helpers]
|
||||
}
|
||||
|
||||
export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
||||
|
||||
export function createSSRTransformContext(options: CompilerOptions) {
|
||||
function createSSRTransformContext(
|
||||
options: CompilerOptions,
|
||||
helpers: Set<symbol> = new Set()
|
||||
) {
|
||||
const body: BlockStatement['body'] = []
|
||||
let currentString: TemplateLiteral | null = null
|
||||
|
||||
return {
|
||||
options,
|
||||
body,
|
||||
helpers,
|
||||
helper<T extends symbol>(name: T): T {
|
||||
helpers.add(name)
|
||||
return name
|
||||
},
|
||||
pushStringPart(part: TemplateLiteral['elements'][0]) {
|
||||
if (!currentString) {
|
||||
const currentCall = createCallExpression(`_push`)
|
||||
@@ -71,6 +81,13 @@ export function createSSRTransformContext(options: CompilerOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
export function createChildContext(
|
||||
parent: SSRTransformContext
|
||||
): SSRTransformContext {
|
||||
// ensure child inherits parent helpers
|
||||
return createSSRTransformContext(parent.options, parent.helpers)
|
||||
}
|
||||
|
||||
export function processChildren(
|
||||
children: TemplateChildNode[],
|
||||
context: SSRTransformContext
|
||||
@@ -100,11 +117,13 @@ export function processChildren(
|
||||
} else if (child.type === NodeTypes.TEXT) {
|
||||
context.pushStringPart(escapeHtml(child.content))
|
||||
} else if (child.type === NodeTypes.INTERPOLATION) {
|
||||
context.pushStringPart(createCallExpression(INTERPOLATE, [child.content]))
|
||||
context.pushStringPart(
|
||||
createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
|
||||
)
|
||||
} else if (child.type === NodeTypes.IF) {
|
||||
processIf(child, context)
|
||||
} else if (child.type === NodeTypes.FOR) {
|
||||
// TODO
|
||||
processFor(child, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
import { NodeTransform } from '@vue/compiler-dom'
|
||||
import {
|
||||
createStructuralDirectiveTransform,
|
||||
ForNode,
|
||||
processForNode
|
||||
} from '@vue/compiler-dom'
|
||||
import { SSRTransformContext } from '../ssrCodegenTransform'
|
||||
|
||||
export const ssrTransformFor: NodeTransform = () => {}
|
||||
// Plugin for the first transform pass, which simply constructs the AST node
|
||||
export const ssrTransformFor = createStructuralDirectiveTransform(
|
||||
'for',
|
||||
processForNode
|
||||
)
|
||||
|
||||
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
||||
// codegen nodes.
|
||||
export function processFor(node: ForNode, context: SSRTransformContext) {}
|
||||
|
||||
@@ -11,12 +11,11 @@ import {
|
||||
} from '@vue/compiler-dom'
|
||||
import {
|
||||
SSRTransformContext,
|
||||
createSSRTransformContext,
|
||||
createChildContext,
|
||||
processChildren
|
||||
} from '../ssrCodegenTransform'
|
||||
|
||||
// This is the plugin for the first transform pass, which simply constructs the
|
||||
// if node and its branches.
|
||||
// Plugin for the first transform pass, which simply constructs the AST node
|
||||
export const ssrTransformIf = createStructuralDirectiveTransform(
|
||||
/^(if|else|else-if)$/,
|
||||
processIfBranches
|
||||
@@ -64,7 +63,7 @@ function processIfBranch(
|
||||
// TODO optimize away nested fragments when the only child is a ForNode
|
||||
const needFragmentWrapper =
|
||||
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
|
||||
const childContext = createSSRTransformContext(context.options)
|
||||
const childContext = createChildContext(context)
|
||||
if (needFragmentWrapper) {
|
||||
childContext.pushStringPart(`<!---->`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user