Skip to content

Commit efbbd19

Browse files
committed
wip(ssr): initial scaffold for compiler-ssr
1 parent 34e6119 commit efbbd19

26 files changed

+496
-36
lines changed

packages/compiler-core/src/ast.ts

+47-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ export const enum NodeTypes {
4545
JS_FUNCTION_EXPRESSION,
4646
JS_SEQUENCE_EXPRESSION,
4747
JS_CONDITIONAL_EXPRESSION,
48-
JS_CACHE_EXPRESSION
48+
JS_CACHE_EXPRESSION,
49+
50+
// ssr codegen
51+
JS_BLOCK_STATEMENT,
52+
JS_TEMPLATE_LITERAL,
53+
JS_IF_STATEMENT
4954
}
5055

5156
export const enum ElementTypes {
@@ -97,7 +102,7 @@ export interface RootNode extends Node {
97102
hoists: JSChildNode[]
98103
imports: ImportItem[]
99104
cached: number
100-
codegenNode: TemplateChildNode | JSChildNode | undefined
105+
codegenNode: TemplateChildNode | JSChildNode | BlockStatement | undefined
101106
}
102107

103108
export type ElementNode =
@@ -130,6 +135,7 @@ export interface PlainElementNode extends BaseElementNode {
130135
| CacheExpression // when cached by v-once
131136
| SequenceExpression // when turned into a block
132137
| undefined
138+
ssrCodegenNode?: TemplateLiteral
133139
}
134140

135141
export interface ComponentNode extends BaseElementNode {
@@ -147,7 +153,7 @@ export interface SlotOutletNode extends BaseElementNode {
147153

148154
export interface TemplateNode extends BaseElementNode {
149155
tagType: ElementTypes.TEMPLATE
150-
codegenNode: ElementCodegenNode | undefined | CacheExpression
156+
// TemplateNode is a container type that always gets compiled away
151157
}
152158

153159
export interface TextNode extends Node {
@@ -232,9 +238,12 @@ export interface TextCallNode extends Node {
232238
codegenNode: CallExpression
233239
}
234240

241+
// JS Node Types ---------------------------------------------------------------
242+
235243
// We also include a number of JavaScript AST nodes for code generation.
236244
// The AST is an intentionally minimal subset just to meet the exact needs of
237245
// Vue render function generation.
246+
238247
export type JSChildNode =
239248
| CallExpression
240249
| ObjectExpression
@@ -252,6 +261,7 @@ export interface CallExpression extends Node {
252261
| string
253262
| symbol
254263
| JSChildNode
264+
| SSRCodegenNode
255265
| TemplateChildNode
256266
| TemplateChildNode[])[]
257267
}
@@ -275,8 +285,10 @@ export interface ArrayExpression extends Node {
275285
export interface FunctionExpression extends Node {
276286
type: NodeTypes.JS_FUNCTION_EXPRESSION
277287
params: ExpressionNode | ExpressionNode[] | undefined
278-
returns: TemplateChildNode | TemplateChildNode[] | JSChildNode
288+
returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode
289+
body?: BlockStatement
279290
newline: boolean
291+
// so that codegen knows it needs to generate ScopeId wrapper
280292
isSlot: boolean
281293
}
282294

@@ -299,6 +311,27 @@ export interface CacheExpression extends Node {
299311
isVNode: boolean
300312
}
301313

314+
// SSR-specific Node Types -----------------------------------------------------
315+
316+
export type SSRCodegenNode = BlockStatement | TemplateLiteral | IfStatement
317+
318+
export interface BlockStatement extends Node {
319+
type: NodeTypes.JS_BLOCK_STATEMENT
320+
body: (JSChildNode | IfStatement)[]
321+
}
322+
323+
export interface TemplateLiteral extends Node {
324+
type: NodeTypes.JS_TEMPLATE_LITERAL
325+
elements: (string | JSChildNode)[]
326+
}
327+
328+
export interface IfStatement extends Node {
329+
type: NodeTypes.JS_IF_STATEMENT
330+
test: ExpressionNode
331+
consequent: BlockStatement
332+
alternate: IfStatement | BlockStatement
333+
}
334+
302335
// Codegen Node Types ----------------------------------------------------------
303336

304337
// createVNode(...)
@@ -637,3 +670,13 @@ export function createCacheExpression(
637670
loc: locStub
638671
}
639672
}
673+
674+
export function createTemplateLiteral(
675+
elements: TemplateLiteral['elements']
676+
): TemplateLiteral {
677+
return {
678+
type: NodeTypes.JS_TEMPLATE_LITERAL,
679+
elements,
680+
loc: locStub
681+
}
682+
}

packages/compiler-core/src/codegen.ts

+64-19
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
SequenceExpression,
1919
ConditionalExpression,
2020
CacheExpression,
21-
locStub
21+
locStub,
22+
SSRCodegenNode,
23+
TemplateLiteral
2224
} from './ast'
2325
import { SourceMapGenerator, RawSourceMap } from 'source-map'
2426
import {
@@ -44,7 +46,7 @@ import {
4446
} from './runtimeHelpers'
4547
import { ImportItem } from './transform'
4648

47-
type CodegenNode = TemplateChildNode | JSChildNode
49+
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
4850

4951
export interface CodegenResult {
5052
code: string
@@ -74,7 +76,8 @@ function createCodegenContext(
7476
prefixIdentifiers = mode === 'module' || mode === 'cjs',
7577
sourceMap = false,
7678
filename = `template.vue.html`,
77-
scopeId = null
79+
scopeId = null,
80+
ssr = false
7881
}: CodegenOptions
7982
): CodegenContext {
8083
const context: CodegenContext = {
@@ -83,6 +86,7 @@ function createCodegenContext(
8386
sourceMap,
8487
filename,
8588
scopeId,
89+
ssr,
8690
source: ast.loc.source,
8791
code: ``,
8892
column: 1,
@@ -169,7 +173,8 @@ export function generate(
169173
indent,
170174
deindent,
171175
newline,
172-
scopeId
176+
scopeId,
177+
ssr
173178
} = context
174179
const hasHelpers = ast.helpers.length > 0
175180
const useWithBlock = !prefixIdentifiers && mode !== 'module'
@@ -231,10 +236,14 @@ export function generate(
231236
}
232237

233238
// enter render function
234-
if (genScopeId) {
239+
if (genScopeId && !ssr) {
235240
push(`const render = withId(`)
236241
}
237-
push(`function render() {`)
242+
if (!ssr) {
243+
push(`function render() {`)
244+
} else {
245+
push(`function ssrRender(_ctx, _push, _parent) {`)
246+
}
238247
indent()
239248

240249
if (useWithBlock) {
@@ -255,7 +264,7 @@ export function generate(
255264
}
256265
newline()
257266
}
258-
} else {
267+
} else if (!ssr) {
259268
push(`const _ctx = this`)
260269
if (ast.cached > 0) {
261270
newline()
@@ -276,7 +285,9 @@ export function generate(
276285
}
277286

278287
// generate the VNode tree expression
279-
push(`return `)
288+
if (!ssr) {
289+
push(`return `)
290+
}
280291
if (ast.codegenNode) {
281292
genNode(ast.codegenNode, context)
282293
} else {
@@ -291,7 +302,7 @@ export function generate(
291302
deindent()
292303
push(`}`)
293304

294-
if (genScopeId) {
305+
if (genScopeId && !ssr) {
295306
push(`)`)
296307
}
297308

@@ -325,7 +336,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
325336
return
326337
}
327338
const { push, newline, helper, scopeId, mode } = context
328-
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
339+
const genScopeId = !__BROWSER__ && scopeId != null && mode !== 'function'
329340
newline()
330341

331342
// push scope Id before initilaizing hoisted vnodes so that these vnodes
@@ -469,6 +480,18 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
469480
case NodeTypes.JS_CACHE_EXPRESSION:
470481
genCacheExpression(node, context)
471482
break
483+
484+
// SSR only types
485+
case NodeTypes.JS_BLOCK_STATEMENT:
486+
!__BROWSER__ && genNodeList(node.body, context, true)
487+
break
488+
case NodeTypes.JS_TEMPLATE_LITERAL:
489+
!__BROWSER__ && genTemplateLiteral(node, context)
490+
break
491+
case NodeTypes.JS_IF_STATEMENT:
492+
// TODO
493+
break
494+
472495
/* istanbul ignore next */
473496
default:
474497
if (__DEV__) {
@@ -589,10 +612,10 @@ function genFunctionExpression(
589612
context: CodegenContext
590613
) {
591614
const { push, indent, deindent, scopeId, mode } = context
592-
const { params, returns, newline, isSlot } = node
615+
const { params, returns, body, newline, isSlot } = node
593616
// slot functions also need to push scopeId before rendering its content
594617
const genScopeId =
595-
!__BROWSER__ && isSlot && scopeId != null && mode === 'module'
618+
!__BROWSER__ && isSlot && scopeId != null && mode !== 'function'
596619

597620
if (genScopeId) {
598621
push(`withId(`)
@@ -604,17 +627,23 @@ function genFunctionExpression(
604627
genNode(params, context)
605628
}
606629
push(`) => `)
607-
if (newline) {
630+
if (newline || body) {
608631
push(`{`)
609632
indent()
610-
push(`return `)
611633
}
612-
if (isArray(returns)) {
613-
genNodeListAsArray(returns, context)
614-
} else {
615-
genNode(returns, context)
634+
if (returns) {
635+
if (newline) {
636+
push(`return `)
637+
}
638+
if (isArray(returns)) {
639+
genNodeListAsArray(returns, context)
640+
} else {
641+
genNode(returns, context)
642+
}
643+
} else if (body) {
644+
genNode(body, context)
616645
}
617-
if (newline) {
646+
if (newline || body) {
618647
deindent()
619648
push(`}`)
620649
}
@@ -686,3 +715,19 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
686715
}
687716
push(`)`)
688717
}
718+
719+
function genTemplateLiteral(node: TemplateLiteral, context: CodegenContext) {
720+
const { push } = context
721+
push('`')
722+
for (let i = 0; i < node.elements.length; i++) {
723+
const e = node.elements[i]
724+
if (isString(e)) {
725+
push(e.replace(/`/g, '\\`'))
726+
} else {
727+
push('${')
728+
genNode(e, context)
729+
push('}')
730+
}
731+
}
732+
push('`')
733+
}

packages/compiler-core/src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ export { registerRuntimeHelpers } from './runtimeHelpers'
3131
export { transformModel } from './transforms/vModel'
3232
export { transformOn } from './transforms/vOn'
3333

34+
// exported for compiler-ssr
35+
export { transformExpression } from './transforms/transformExpression'
36+
export { trackVForSlotScopes, trackSlotScopes } from './transforms/vSlot'
37+
export { buildProps } from './transforms/transformElement'
38+
3439
// utility, but need to rewrite typing to avoid dts relying on @vue/shared
3540
import { generateCodeFrame as _genCodeFrame } from '@vue/shared'
3641
const generateCodeFrame = _genCodeFrame as (

packages/compiler-core/src/options.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface ParserOptions {
2929

3030
export interface TransformOptions {
3131
nodeTransforms?: NodeTransform[]
32-
directiveTransforms?: { [name: string]: DirectiveTransform }
32+
directiveTransforms?: { [name: string]: DirectiveTransform | undefined }
3333
isBuiltInComponent?: (tag: string) => symbol | void
3434
// Transform expressions like {{ foo }} to `_ctx.foo`.
3535
// - This is force-enabled in module mode, since modules are by default strict
@@ -76,6 +76,8 @@ export interface CodegenOptions {
7676
filename?: string
7777
// SFC scoped styles ID
7878
scopeId?: string | null
79+
// generate SSR specific code?
80+
ssr?: boolean
7981
}
8082

8183
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions

packages/compiler-dom/src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import { transformOn } from './transforms/vOn'
1818
import { transformShow } from './transforms/vShow'
1919
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
2020

21-
const parserOptions = __BROWSER__ ? parserOptionsMinimal : parserOptionsStandard
21+
export const parserOptions = __BROWSER__
22+
? parserOptionsMinimal
23+
: parserOptionsStandard
2224

2325
export function compile(
2426
template: string,

packages/compiler-ssr/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
},
2828
"homepage": "https://github.com/vuejs/vue/tree/dev/packages/compiler-ssr#readme",
2929
"dependencies": {
30-
"@vue/compiler-core": "3.0.0-alpha.4"
30+
"@vue/compiler-dom": "3.0.0-alpha.4"
3131
}
3232
}

0 commit comments

Comments
 (0)