Skip to content

Commit

Permalink
feat: 收敛 renderer 的参数
Browse files Browse the repository at this point in the history
  • Loading branch information
meixg committed Sep 13, 2021
1 parent 7ed93c1 commit a6deec8
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 28 deletions.
2 changes: 1 addition & 1 deletion bin/debug-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ fs.writeFileSync(path.resolve(__dirname, './output.js'), res)
const Component = require('./component')
const render = require('./output')

const html = render({}, false, null, 'div', {}, { ComponentClass: Component })
const html = render({}, { ComponentClass: Component })

console.log(html)
4 changes: 3 additions & 1 deletion bin/render-by-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ if (fs.existsSync(ssrSpecPath)) {
}

const render = require(join(caseRoot, `${caseName}/output/ssr.js`))
const html = render(...getRenderArguments(caseName, caseRoot), { context: ssrSpec && ssrSpec.context })
const html = render(...getRenderArguments(caseName, caseRoot, {
parentCtx: { context: ssrSpec && ssrSpec.context }
}))

process.stdout.write(html)
6 changes: 5 additions & 1 deletion src/ast/renderer-ast-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 例如:new AssignmentStatement(new Identifier('html'), new Literal('foo')) 可以简写为 ASSIGN(I('html), L('foo))
*/

import { SyntaxKind, SyntaxNode, Block, MapLiteral, UnaryOperator, UnaryExpression, NewExpression, VariableDefinition, ReturnStatement, BinaryOperator, If, Null, Undefined, AssignmentStatement, Statement, Expression, Identifier, ExpressionStatement, BinaryExpression, Literal, TryStatement, CatchClause } from './renderer-ast-dfn'
import { SyntaxKind, SyntaxNode, Block, MapLiteral, UnaryOperator, UnaryExpression, NewExpression, VariableDefinition, ReturnStatement, BinaryOperator, If, Null, Undefined, AssignmentStatement, Statement, Expression, Identifier, ExpressionStatement, BinaryExpression, Literal, TryStatement, CatchClause, ConditionalExpression } from './renderer-ast-dfn'

export function createHTMLLiteralAppend (html: string) {
return STATEMENT(BINARY(I('html'), '+=', L(html)))
Expand Down Expand Up @@ -35,6 +35,10 @@ export function createTryStatement (block: Statement[], param: Identifier, body:
return new TryStatement(block, new CatchClause(param, body))
}

export function createDefineWithDefaultValue (varName: string, value: Expression, defaultValue: Expression) {
return DEF(varName, new ConditionalExpression(BINARY(value, '==', NULL), defaultValue, value))
}

export function L (val: any) {
return Literal.create(val)
}
Expand Down
13 changes: 9 additions & 4 deletions src/compilers/anode-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,18 @@ export class ANodeCompiler<T extends 'none' | 'typed'> {
}

// get and call renderer
const args = [this.childRenderData(aNode), ndo, I('parentCtx'), L(aNode.tagName), childSlots]
const mapItems = [
[I('noDataOutput'), ndo],
[I('parentCtx'), I('parentCtx')],
[I('tagName'), L(aNode.tagName)],
[I('slots'), childSlots]
] as ConstructorParameters<typeof MapLiteral>[0]
if (this.useProvidedComponentClass) {
assert(ChildComponentClassName !== '')
args.push(new MapLiteral([
[I('ComponentClass'), I(ChildComponentClassName)]
]))
mapItems.push([I('ComponentClass'), I(ChildComponentClassName)])
}

const args = [this.childRenderData(aNode), new MapLiteral(mapItems)]
const childRenderCall = new FunctionCall(
new ComponentRendererReference(ref, L(aNode.tagName)),
args
Expand Down
10 changes: 5 additions & 5 deletions src/compilers/renderer-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ANodeCompiler } from './anode-compiler'
import { ComponentInfo } from '../models/component-info'
import { RenderOptions } from './renderer-options'
import { FunctionDefinition, ComputedCall, Foreach, FunctionCall, MapLiteral, If, CreateComponentInstance, ImportHelper, ComponentReferenceLiteral, ConditionalExpression, BinaryExpression } from '../ast/renderer-ast-dfn'
import { EMPTY_MAP, STATEMENT, NEW, BINARY, ASSIGN, DEF, RETURN, createDefaultValue, L, I, NULL, UNDEFINED, createTryStatement } from '../ast/renderer-ast-util'
import { EMPTY_MAP, STATEMENT, NEW, BINARY, ASSIGN, DEF, RETURN, createDefaultValue, L, I, NULL, UNDEFINED, createTryStatement, createDefineWithDefaultValue } from '../ast/renderer-ast-util'
import { IDGenerator } from '../utils/id-generator'
import { mergeLiteralAdd } from '../optimizers/merge-literal-add'

Expand All @@ -28,10 +28,6 @@ export class RendererCompiler {
public compileToRenderer (componentInfo: ComponentInfo) {
const args = [
DEF('data'),
DEF('noDataOutput', L(false)),
DEF('parentCtx', NULL),
DEF('tagName', L('div')),
DEF('slots', EMPTY_MAP),

// 参数太多了,后续要增加的参数统一收敛到这里
DEF('info', L({}))
Expand All @@ -52,6 +48,10 @@ export class RendererCompiler {
}

// get params from info
body.push(createDefineWithDefaultValue('noDataOutput', BINARY(I('info'), '.', I('noDataOutput')), L(false)))
body.push(createDefineWithDefaultValue('parentCtx', BINARY(I('info'), '.', I('parentCtx')), NULL))
body.push(createDefineWithDefaultValue('tagName', BINARY(I('info'), '.', I('tagName')), L('div')))
body.push(createDefineWithDefaultValue('slots', BINARY(I('info'), '.', I('slots')), EMPTY_MAP))
if (this.options.useProvidedComponentClass) {
body.push(DEF('ComponentClass', new BinaryExpression(I('info'), '.', I('ComponentClass'))))
}
Expand Down
9 changes: 6 additions & 3 deletions src/fixtures/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import debugFactory from 'debug'
import { compileToRenderer } from '../index'
import mkdirp from 'mkdirp'
import type { RenderOptions } from '../compilers/renderer-options'
import type { Renderer } from '../models/renderer'

const debug = debugFactory('case')
export const caseRoots = [
Expand Down Expand Up @@ -140,15 +141,17 @@ export function readCaseData (caseName: string, caseRoot: string) {
return JSON.parse(readFileSync(dataPath, 'utf8'))
}

export function getRenderArguments (caseName: string, caseRoot: string) {
export function getRenderArguments (caseName: string, caseRoot: string, info: Partial<Parameters<Renderer>['1']> = {}): Parameters<Renderer> {
const data = readCaseData(caseName, caseRoot)
const noDataOutput = /-ndo$/.test(caseName)
return [data, noDataOutput]
return [data, Object.assign({
noDataOutput
}, info)]
}

export function renderOnthefly (caseName: string, caseRoot: string) {
const render = compileCaseToRenderer(caseName, caseRoot)
const data = readCaseData(caseName, caseRoot)
const noDataOutput = /-ndo$/.test(caseName)
return render(data, noDataOutput)
return render(data, { noDataOutput })
}
13 changes: 12 additions & 1 deletion src/models/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
/**
* SanProject#compileToRenderer() 输出的 renderer 的类型声明
*/

import type { ComponentClass } from './component'
import type { GlobalContext } from './global-context'

export interface Renderer {
(data: { [key: string]: any }, noDataOutput?: boolean): string
(data: { [key: string]: any }, info?: {
noDataOutput?: boolean,
parentCtx?: {
context?: GlobalContext
},
tagName?: string,
ComponentClass?: ComponentClass
}): string
}
4 changes: 2 additions & 2 deletions src/target-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ export default class ToJSCompiler implements TargetCodeGenerator {
sanSSRResolver.setPrototype(info.id, info.componentClass.prototype)
sanSSRResolver.setRenderer(info.id, resolvedRenderer)
}
return (data: any, noDataOutput: boolean = false) => {
return (data, info) => {
const render = sanSSRResolver.getRenderer({ id: entryComponentInfo.id })
return render(data, noDataOutput)
return render(data, info)
}
}

Expand Down
13 changes: 8 additions & 5 deletions test/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { RenderOptions } from '../src/index'
import { existsSync } from 'fs'
import { execSync } from 'child_process'
import type { GlobalContext } from '../src/models/global-context'
import type { Renderer } from '../src/models/renderer'

export interface SsrSpecConfig {
enabled: {
Expand Down Expand Up @@ -81,9 +82,9 @@ for (const { caseName, caseRoot } of cases) {
ssrSpec.afterHook && ssrSpec.afterHook('jssrc')

// render
const render = require(join(caseRoot, caseName, 'output', folderName, 'ssr.js'))
const render = require(join(caseRoot, caseName, 'output', folderName, 'ssr.js')) as Renderer
// 测试在 strict mode,因此需要手动传入 require
const got = render(...getRenderArguments(caseName, caseRoot), { context: ssrSpec && ssrSpec.context })
const got = render(...getRenderArguments(caseName, caseRoot, { parentCtx: { context: ssrSpec && ssrSpec.context } }))
const [data, html] = parseSanHTML(got)

expect(data).toEqual(expectedData)
Expand All @@ -102,14 +103,16 @@ for (const { caseName, caseRoot } of cases) {
ssrSpec.afterHook && ssrSpec.afterHook('comsrc')

// render
const render = require(join(caseRoot, caseName, 'output', folderName, 'ssr.js'))
const render = require(join(caseRoot, caseName, 'output', folderName, 'ssr.js')) as Renderer

// 测试在 strict mode,因此需要手动传入 require
const info = {} as any
const info = {
parentCtx: { context: ssrSpec && ssrSpec.context }
} as Parameters<Renderer>[1]
if (ssrSpec.compileOptions.useProvidedComponentClass) {
info.ComponentClass = require(join(caseRoot, caseName, 'component.js'))
}
const got = render(...getRenderArguments(caseName, caseRoot), { context: ssrSpec && ssrSpec.context }, 'div', {}, info)
const got = render(...getRenderArguments(caseName, caseRoot, info))
const [data, html] = parseSanHTML(got)

expect(data).toEqual(expectedData)
Expand Down
12 changes: 9 additions & 3 deletions test/unit/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ describe('compileToRenderer', function () {
const render = compileToRenderer(ComponentClass)

expect(render).toBeInstanceOf(Function)
expect(render({}, true)).toEqual('<span>A</span>')
expect(render({}, {
noDataOutput: true
})).toEqual('<span>A</span>')
})

it('should compile to a renderer function which accepts data', function () {
const ComponentClass = defineComponent({ template: '<span>name: {{name}}</span>' })
const render = compileToRenderer(ComponentClass)

expect(render).toBeInstanceOf(Function)
expect(render({ name: 'Harttle' }, true)).toEqual('<span>name: Harttle</span>')
expect(render({ name: 'Harttle' }, {
noDataOutput: true
})).toEqual('<span>name: Harttle</span>')
})

it('should run inited only in run time', function () {
Expand All @@ -34,7 +38,9 @@ describe('compileToRenderer', function () {
const render = compileToRenderer(ComponentClass)

expect(inited).not.toBeCalled()
expect(render({}, true)).toEqual('<div>a</div>')
expect(render({}, {
noDataOutput: true
})).toEqual('<div>a</div>')
expect(inited).toBeCalledTimes(1)
})
})
8 changes: 6 additions & 2 deletions test/unit/models/san-project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ describe('SanProject', function () {
const render = proj.compileToRenderer(require(resolve(stubRoot, './name.comp.js')))

expect(render).toBeInstanceOf(Function)
expect(render({ name: 'Harttle' }, true)).toEqual('<div>name: Harttle</div>')
expect(render({ name: 'Harttle' }, {
noDataOutput: true
})).toEqual('<div>name: Harttle</div>')
})

it('the noDataOutput parameter should be optional and default to false', function () {
Expand Down Expand Up @@ -140,7 +142,9 @@ describe('SanProject', function () {
const render = proj.compileToRenderer(require(resolve(stubRoot, './name.comp.js')))

expect(render).toBeInstanceOf(Function)
expect(render({ name: 'Harttle' }, true)).toEqual('<div>name: Harttle</div>')
expect(render({ name: 'Harttle' }, {
noDataOutput: true
})).toEqual('<div>name: Harttle</div>')
})

it('should remove modules', function () {
Expand Down

0 comments on commit a6deec8

Please sign in to comment.