• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

baidu / san-ssr / 3824729513

pending completion
3824729513

Pull #163

github

GitHub
Merge e05e440a2 into 5feaebca6
Pull Request #163: chore(deps): bump json5 and tsconfig-paths

1105 of 1107 branches covered (99.82%)

Branch coverage included in aggregate %.

1853 of 1854 relevant lines covered (99.95%)

4190.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/compilers/renderer-compiler.ts
1
/**
2
 * 把组件(ComponentInfo)编译成 renderer 函数(render AST 形式)
3
 *
4
 * 每个 ComponentInfo 对应于一个 San 组件定义,对应一个 SSR 的 renderer 函数。
5
 * 这个函数接受数据,返回 HTML。
6
 */
7
import { ANodeCompiler } from './anode-compiler'
8
import { ComponentInfo } from '../models/component-info'
9
import { RenderOptions } from './renderer-options'
10
import {
11
    FunctionDefinition, ComputedCall, Foreach, FunctionCall, MapLiteral, If, CreateComponentInstance, ImportHelper,
12
    ComponentReferenceLiteral, ConditionalExpression, BinaryExpression, CreateComponentPrototype, Else
13
} from '../ast/renderer-ast-dfn'
14
import {
15
    EMPTY_MAP, STATEMENT, NEW, BINARY, ASSIGN, DEF, RETURN, createDefaultValue, L, I, NULL, UNDEFINED,
16
    createTryStatement, createDefineWithDefaultValue
17
} from '../ast/renderer-ast-util'
18
import { IDGenerator } from '../utils/id-generator'
19
import { mergeLiteralAdd } from '../optimizers/merge-literal-add'
20

21
/**
22
 * 每个 ComponentClass 对应一个 Render 函数,由 RendererCompiler 生成。
23
 */
24
export class RendererCompiler {
25
    private id = new IDGenerator()
785✔
26

27
    constructor (
28
        private options: RenderOptions
29
    ) {}
30

31
    /**
32
     * 把 ComponentInfo 编译成函数源码,返回 Renderer 函数的 AST
33
     */
34
    public compileToRenderer (componentInfo: ComponentInfo) {
35
        const args = [
785✔
36
            DEF('data'),
37

38
            // 参数太多了,后续要增加的参数统一收敛到这里
39
            DEF('...info')
40
        ]
41
        const fn = new FunctionDefinition(this.options.functionName || '', args,
785✔
42
            componentInfo.componentType === 'template'
785✔
43
                ? this.compileTemplateComponentRendererBody(componentInfo)
44
                : this.compileComponentRendererBody(componentInfo)
45
        )
46
        mergeLiteralAdd(fn)
785✔
47
        return fn
785✔
48
    }
49

50
    private compileComponentRendererBody (info: ComponentInfo) {
51
        const body = []
761✔
52
        // 没有 ANode 的组件,比如 load-success 样例
53
        if (!info.root) {
761✔
54
            body.push(RETURN(L('')))
3✔
55
            return body
3✔
56
        }
57

58
        // 兼容多参数的情况
59
        body.push(new If(
758✔
60
            new BinaryExpression(
61
                BINARY(I('info'), '.', I('length')),
62
                '===',
63
                L(1)
64
            ),
65
            [ASSIGN(I('info'), BINARY(
66
                BINARY(I('info'), '[]', L(0)),
67
                '||',
68
                L({})
69
            ))]
70
        ))
71
        body.push(new Else([
758✔
72
            ASSIGN(I('info'), new MapLiteral([
73
                [I('noDataOutput'), BINARY(I('info'), '[]', L(1))],
74
                [I('parentCtx'), BINARY(I('info'), '[]', L(2))],
75
                [I('tagName'), BINARY(I('info'), '[]', L(3))],
76
                [I('slots'), BINARY(I('info'), '[]', L(4))]
77
            ]))
78
        ]))
79

80
        // get params from info
81
        body.push(createDefineWithDefaultValue('noDataOutput', BINARY(I('info'), '.', I('noDataOutput')), L(false)))
758✔
82
        body.push(createDefineWithDefaultValue('parentCtx', BINARY(I('info'), '.', I('parentCtx')), NULL))
758✔
83
        body.push(createDefineWithDefaultValue('tagName', BINARY(I('info'), '.', I('tagName')), L('div')))
758✔
84
        body.push(createDefineWithDefaultValue('slots', BINARY(I('info'), '.', I('slots')), EMPTY_MAP))
758✔
85

86
        // helper
87
        body.push(new ImportHelper('_'))
758✔
88
        body.push(new ImportHelper('SanSSRData'))
758✔
89

90
        if (this.options.useProvidedComponentClass) {
758✔
91
            body.push(STATEMENT(new CreateComponentPrototype(info)))
15✔
92
        }
93

94
        // context
95
        body.push(this.compileGenInstance(info))
758✔
96
        body.push(...this.compileContext(info))
758✔
97

98
        // instance preraration
99
        if (info.hasMethod('initData')) {
758✔
100
            body.push(...this.emitInitData())
63✔
101
        }
102

103
        // call inited
104
        if (info.hasMethod('inited')) {
758✔
105
            body.push(createTryStatement(
38✔
106
                [STATEMENT(new FunctionCall(BINARY(I('instance'), '.', I('inited')), []))],
107
                I('e'),
108
                [STATEMENT(new FunctionCall(BINARY(I('_'), '.', I('handleError')), [
109
                    I('e'),
110
                    I('instance'),
111
                    L('hook:inited')
112
                ]))]
113
            ))
114
        }
115

116
        // calc computed
117
        for (const name of info.getComputedNames()) {
758✔
118
            body.push(ASSIGN(BINARY(I('data'), '[]', L(name)), new ComputedCall(name)))
39✔
119
        }
120

121
        body.push(ASSIGN(
758✔
122
            BINARY(I('instance'), '.', BINARY(I('lifeCycle'), '.', I('inited'))),
123
            I('true')
124
        ))
125

126
        body.push(DEF('html', L('')))
758✔
127
        body.push(ASSIGN(I('parentCtx'), I('ctx')))
758✔
128
        const aNodeCompiler = new ANodeCompiler(
758✔
129
            info, !!this.options.ssrOnly, this.id, this.options.useProvidedComponentClass
130
        )
131
        body.push(...aNodeCompiler.compile(info.root, true))
758✔
132

133
        body.push(RETURN(I('html')))
758✔
134
        return body
758✔
135
    }
136

137
    private compileTemplateComponentRendererBody (info: ComponentInfo) {
138
        const body = []
24✔
139
        // 没有 ANode 的组件,比如 load-success 样例
140
        if (!info.root) {
24✔
141
            body.push(RETURN(L('')))
1✔
142
            return body
1✔
143
        }
144

145
        // 兼容多参数的情况
146
        body.push(new If(
23✔
147
            new BinaryExpression(
148
                BINARY(I('info'), '.', I('length')),
149
                '===',
150
                L(1)
151
            ),
152
            [ASSIGN(I('info'), BINARY(
153
                BINARY(I('info'), '[]', L(0)),
154
                '||',
155
                L({})
156
            ))]
157
        ))
158
        body.push(new Else([
23✔
159
            ASSIGN(I('info'), new MapLiteral([
160
                [I('noDataOutput'), BINARY(I('info'), '[]', L(1))],
161
                [I('parentCtx'), BINARY(I('info'), '[]', L(2))],
162
                [I('slots'), BINARY(I('info'), '[]', L(4))]
163
            ]))
164
        ]))
165

166
        // get params from info
167
        body.push(createDefineWithDefaultValue('noDataOutput', BINARY(I('info'), '.', I('noDataOutput')), L(false)))
23✔
168
        body.push(createDefineWithDefaultValue('parentCtx', BINARY(I('info'), '.', I('parentCtx')), NULL))
23✔
169
        body.push(createDefineWithDefaultValue('slots', BINARY(I('info'), '.', I('slots')), EMPTY_MAP))
23✔
170

171
        // helper
172
        body.push(new ImportHelper('_'))
23✔
173

174
        // instance preraration
175
        if (info.hasMethod('initData')) {
23✔
176
            if (this.options.useProvidedComponentClass) {
5✔
177
                body.push(STATEMENT(new CreateComponentPrototype(info)))
2✔
178
            }
179
            // context
180
            body.push(DEF('instance', new CreateComponentInstance(info)))
5✔
181
            body.push(...this.compileTemplateComponentContext())
5✔
182
            body.push(...this.emitInitData())
5✔
183
        } else {
184
            body.push(DEF('instance', I('{}')))
18✔
185
            body.push(...this.compileTemplateComponentContext())
18✔
186
        }
187

188
        body.push(DEF('html', L('')))
23✔
189
        body.push(ASSIGN(I('parentCtx'), I('ctx')))
23✔
190
        const aNodeCompiler = new ANodeCompiler(
23✔
191
            info, !!this.options.ssrOnly, this.id, this.options.useProvidedComponentClass
192
        )
193
        body.push(...aNodeCompiler.compile(info.root, true))
23✔
194

195
        body.push(RETURN(I('html')))
23✔
196
        return body
23✔
197
    }
198

199
    private compileGenInstance (info: ComponentInfo) {
200
        return DEF('instance', new CreateComponentInstance(info))
758✔
201
    }
202

203
    private compileContext (info: ComponentInfo) {
204
        const refs = info.hasDynamicComponent()
758✔
205
            ? new MapLiteral(
206
                [...info.childComponents.entries()]
207
                    .map(([key, val]) => [L(key), new ComponentReferenceLiteral(val)])
17✔
208
            )
209
            : EMPTY_MAP
210
        return [
758✔
211
            ASSIGN(
212
                BINARY(I('instance'), '.', I('data')),
213
                NEW(I('SanSSRData'), [I('data'), I('instance')])
214
            ),
215
            ASSIGN(
216
                BINARY(I('instance'), '.', I('sourceSlots')),
217
                new FunctionCall(
218
                    BINARY(I('_'), '.', I('mergeChildSlots')),
219
                    [I('slots')]
220
                )
221
            ),
222
            ASSIGN(
223
                BINARY(I('instance'), '.', I('lifeCycle')),
224
                new MapLiteral([
225
                    [I('compiled'), I('true')],
226
                    [I('inited'), I('false')]
227
                ])
228
            ),
229
            new If(
230
                I('parentCtx'), [ASSIGN(
231
                    BINARY(I('instance'), '.', I('parentComponent')),
232
                    BINARY(I('parentCtx'), '.', I('instance'))
233
                )]
234
            ),
235
            DEF('refs', refs),
236

237
            // 组件级别的 context
238
            DEF('ctx', new MapLiteral([
239
                I('instance'),
240
                I('slots'),
241
                I('data'),
242
                I('parentCtx'),
243
                I('refs'),
244

245
                // 单次渲染级别的 context
246
                // 从最外层一直传下来的,上面可以绑 customRequirePath 等方法
247
                [I('context'), BINARY(I('parentCtx'), '&&', BINARY(I('parentCtx'), '.', I('context')))]
248
            ]))
249
        ]
250
    }
251

252
    private compileTemplateComponentContext () {
253
        return [
23✔
254
            ASSIGN(
255
                BINARY(I('instance'), '.', I('sourceSlots')),
256
                new FunctionCall(
257
                    BINARY(I('_'), '.', I('mergeChildSlots')),
258
                    [I('slots')]
259
                )
260
            ),
261
            DEF('ctx', new MapLiteral([
262
                I('instance'),
263
                I('slots'),
264
                I('data'),
265
                I('parentCtx')
266
            ]))
267
        ]
268
    }
269

270
    /**
271
     * 产出 initData() 的函数调用
272
     *
273
     * 注意即使对于 JSComponentInfo,也不能在编译期调用 initData。
274
     * 因为字面量是无法表示嵌套关系的,详细讨论见:
275
     * https://github.com/baidu/san-ssr/issues/99
276
     */
277
    private emitInitData () {
278
        const item = BINARY(BINARY(I('ctx'), '.', I('data')), '[]', I('key'))
68✔
279

280
        return [
68✔
281
            DEF('initData', undefined),
282
            createTryStatement(
283
                [ASSIGN(I('initData'), new FunctionCall(BINARY(I('instance'), '.', I('initData')), []))],
284
                I('e'),
285
                [STATEMENT(new FunctionCall(BINARY(I('_'), '.', I('handleError')), [
286
                    I('e'),
287
                    I('instance'),
288
                    L('initData')
289
                ]))]
290
            ),
291
            createDefaultValue(I('initData'), new MapLiteral([])),
292
            new Foreach(I('key'), I('value'), I('initData'), [
293
                ASSIGN(item, new ConditionalExpression(BINARY(item, '!==', UNDEFINED), item, I('value')))
294
            ])
295
        ]
296
    }
297
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc