Skip to content

Commit

Permalink
feat: render only component (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
meixg authored Apr 17, 2023
1 parent 5feaebc commit d78c4c9
Show file tree
Hide file tree
Showing 21 changed files with 240 additions and 121 deletions.
2 changes: 1 addition & 1 deletion bin/render-onthefly.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function renderOnTheFly (caseName) {
const caseRoot = caseItem.caseRoot

const ssrSpecPath = join(caseRoot, `${caseName}/ssr-spec.js`)
let ssrSpec
let ssrSpec = {}
if (fs.existsSync(ssrSpecPath)) {
ssrSpec = require(ssrSpecPath)
}
Expand Down
40 changes: 17 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@
"jest": "^27.0.6",
"mkdirp": "^1.0.4",
"mustache": "^4.0.1",
"san": "^3.12.0",
"san-html-cases": "^3.12.4",
"san": "^3.13.0",
"san-html-cases": "^3.13.3",
"san-ssr-target-fake-cmd": "^1.0.0",
"san-ssr-target-fake-esm": "^1.0.0",
"source-map-support": "^0.5.19",
Expand Down
12 changes: 2 additions & 10 deletions src/ast/renderer-ast-dfn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export enum SyntaxKind {
RegexpReplace = 28,
JSONStringify = 29,
HelperCall = 30,
GetRootCtxCall = 31,
ComponentReferenceLiteral = 32,
SlotRendererDefinition = 33,
SlotRenderCall = 34,
Expand All @@ -73,7 +72,7 @@ export enum SyntaxKind {
export type Expression = Identifier | FunctionDefinition | Literal | BinaryExpression | UnaryExpression |
CreateComponentInstance | NewExpression | MapLiteral | ComponentRendererReference | FunctionCall | Null |
Undefined | MapAssign | ArrayIncludes | ConditionalExpression | FilterCall | HelperCall | EncodeURIComponent |
ArrayLiteral | RegexpReplace | JSONStringify | ComputedCall | GetRootCtxCall | ComponentReferenceLiteral |
ArrayLiteral | RegexpReplace | JSONStringify | ComputedCall | ComponentReferenceLiteral |
SlotRendererDefinition | SlotRenderCall | ComponentClassReference | CreateComponentPrototype | Typeof

export type Statement = ReturnStatement | ImportHelper | VariableDefinition | AssignmentStatement | If | ElseIf | Else |
Expand All @@ -86,7 +85,7 @@ export type UnaryOperator = '!' | '~' | '+' | '()' | '-'
export class ArrayLiteral implements SyntaxNode {
public readonly kind = SyntaxKind.ArrayLiteral
constructor (
public items: [Expression, boolean][]
public items: [Expression, boolean][] // [item, isSpread]
) {}
}

Expand Down Expand Up @@ -307,13 +306,6 @@ export class FilterCall implements SyntaxNode {
) {}
}

export class GetRootCtxCall implements SyntaxNode {
public readonly kind = SyntaxKind.GetRootCtxCall
constructor (
public args: Expression[]
) {}
}

export class HelperCall implements SyntaxNode {
public readonly kind = SyntaxKind.HelperCall
constructor (
Expand Down
8 changes: 7 additions & 1 deletion src/ast/renderer-ast-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
Literal,
TryStatement,
CatchClause,
ConditionalExpression
ConditionalExpression,
ArrayLiteral
} from './renderer-ast-dfn'

export function createHTMLLiteralAppend (html: string) {
Expand Down Expand Up @@ -77,6 +78,10 @@ export const UNDEFINED = Undefined.create()

export const CTX_DATA = BINARY(I('ctx'), '.', I('data'))

export function CONDITIONAL (cond: Expression, thenExpr: Expression, elseExpr: Expression) {
return new ConditionalExpression(cond, thenExpr, elseExpr)
}

export function BINARY (lhs: Expression, op: BinaryOperator, rhs: Expression) {
return new BinaryExpression(lhs, op, rhs)
}
Expand Down Expand Up @@ -106,6 +111,7 @@ export function NEW (name: Expression, args: Expression[]) {
}

export const EMPTY_MAP = new MapLiteral([])
export const EMPTY_ARRAY = new ArrayLiteral([])

export function isBlock (node: any): node is Block {
const blocks = [
Expand Down
1 change: 0 additions & 1 deletion src/ast/renderer-ast-walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export function * walk (node: Expression | Statement): Iterable<Expression | Sta
yield * walk(node.trueValue)
break
case SyntaxKind.FilterCall:
case SyntaxKind.GetRootCtxCall:
case SyntaxKind.HelperCall:
for (const arg of node.args) yield * walk(arg)
break
Expand Down
97 changes: 88 additions & 9 deletions src/compilers/anode-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,31 @@ import * as TypeGuards from '../ast/san-ast-type-guards'
import { IDGenerator } from '../utils/id-generator'
import {
JSONStringify, RegexpReplace, Statement, SlotRendererDefinition, ElseIf, Else, MapAssign, Foreach, If, MapLiteral,
ComponentRendererReference, FunctionCall, SlotRenderCall, Expression, GetRootCtxCall, ComponentReferenceLiteral,
ComponentRendererReference, FunctionCall, SlotRenderCall, Expression, ComponentReferenceLiteral,
ComponentClassReference,
VariableDefinition,
ConditionalExpression,
Typeof,
AssignmentStatement
AssignmentStatement,
ArrayLiteral
} from '../ast/renderer-ast-dfn'
import {
CTX_DATA, createHTMLExpressionAppend, createHTMLLiteralAppend, L, I, ASSIGN, STATEMENT, UNARY, DEF, BINARY, RETURN
CTX_DATA,
createHTMLExpressionAppend,
createHTMLLiteralAppend,
L,
I,
ASSIGN,
STATEMENT,
UNARY,
DEF,
BINARY,
RETURN,
CONDITIONAL
} from '../ast/renderer-ast-util'
import { sanExpr, OutputType } from './san-expr-compiler'
import type { RenderOptions } from './renderer-options'
import { RESERVED_NAMES } from './reserved-names'

/**
* ANode 编译
Expand Down Expand Up @@ -202,19 +215,47 @@ export class ANodeCompiler {
dynamicTagName: string | undefined = undefined,
isRootElement: boolean
): Generator<Statement> {
yield * this.elementCompiler.tagStart(aNode, dynamicTagName)
yield * this.elementCompiler.tagStart(
aNode,
dynamicTagName,
isRootElement ? this.compileRootAttrs : undefined
)
if (aNode.tagName === 'script') this.inScript = true
if (isRootElement && !this.ssrOnly && !this.inScript) {
yield new If(UNARY('!', I('noDataOutput')), this.createDataComment())
let dataOutputCondition = UNARY('!', I('noDataOutput')) as Expression
if (
this.componentInfo.componentType !== 'template' &&
(this.componentInfo.ssrType === 'render-only' || this.componentInfo.ssrType === undefined)) {
dataOutputCondition = BINARY(dataOutputCondition, '&&', UNARY('!', I('renderOnly')))
}
yield new If(dataOutputCondition, this.createDataComment())
}

yield * this.elementCompiler.inner(aNode)
this.inScript = false
yield * this.elementCompiler.tagEnd(aNode, dynamicTagName)
}

/**
* add attrs to root element
*/
private * compileRootAttrs () {
yield new If(BINARY(I('attrs'), '&&', BINARY(I('attrs'), '.', I('length'))), [
createHTMLLiteralAppend(' '),
createHTMLExpressionAppend(new FunctionCall(BINARY(I('attrs'), '.', I('join')), [L(' ')]))
])
}

private createDataComment () {
const dataExpr = BINARY(new GetRootCtxCall([I('ctx')]), '.', I('data'))
const dataExpr = CONDITIONAL(
BINARY(I('info'), '.', I(RESERVED_NAMES.renderOnly)),
BINARY(I('ctx'), '.', I('data')),
BINARY(
BINARY(I('info'), '.', I('rootOutputData')),
'||',
BINARY(I('ctx'), '.', I('data'))
)
)
const outputDataExpr = BINARY(I('info'), '.', I('outputData'))
return [
new VariableDefinition('data', dataExpr),
Expand All @@ -223,7 +264,7 @@ export class ANodeCompiler {
I('data'),
new ConditionalExpression(
BINARY(new Typeof(outputDataExpr), '===', L('function')),
new FunctionCall(outputDataExpr, [dataExpr]),
new FunctionCall(outputDataExpr, [I('data')]),
outputDataExpr
)
)
Expand Down Expand Up @@ -277,7 +318,9 @@ export class ANodeCompiler {
}

// data output
const ndo = isRootElement ? I('noDataOutput') : L(true)
const normalNoDataOutput = isRootElement ? I('noDataOutput') : L(true)
const ndo = (this.componentInfo.ssrType === 'render-only' || this.componentInfo.ssrType === undefined)
? CONDITIONAL(I('renderOnly'), L(false), normalNoDataOutput) : normalNoDataOutput

// child component class
let ChildComponentClassName = ''
Expand All @@ -294,12 +337,25 @@ export class ANodeCompiler {
[I('noDataOutput'), ndo],
[I('parentCtx'), I('parentCtx')],
[I('tagName'), L(aNode.tagName)],
[I('slots'), childSlots]
[I('slots'), childSlots],
[I('isChild'), L(true)]
] as ConstructorParameters<typeof MapLiteral>[0]
if (this.useProvidedComponentClass) {
assert(ChildComponentClassName !== '')
mapItems.push([I('ComponentClass'), I(ChildComponentClassName)])
}
if (isRootElement) {
mapItems.push([I('attrs'), I('attrs')])
mapItems.push([
I('rootOutputData'),
BINARY(BINARY(I('info'), '.', I('rootOutputData')), '||', BINARY(I('ctx'), '.', I('data')))
])
}
if (this.componentInfo.ssrType === 'render-only' || this.componentInfo.ssrType === undefined) {
mapItems.push([I(RESERVED_NAMES.renderOnly), this.compileComponentRenderOnlyParam(aNode.tagName)])
} else {
mapItems.push([I(RESERVED_NAMES.renderOnly), L(false)])
}

const args = [this.childRenderData(aNode), new MapLiteral(mapItems)]
const childRenderCall = new FunctionCall(
Expand All @@ -309,6 +365,29 @@ export class ANodeCompiler {
yield createHTMLExpressionAppend(childRenderCall)
}

/**
* renderOnly
* ? typeof (info.preferRenderOnly) === "object" ? {cmpt: [...info.preferRenderOnly.cmpt, "ui-c"]}
* : {cmpt: ["ui-c"]} : false
*/
private compileComponentRenderOnlyParam (tagName: AElement['tagName']) {
const thenValue = CONDITIONAL(
BINARY(new Typeof(BINARY(I('info'), '.', I(RESERVED_NAMES.renderOnly))), '===', L('object')),
new MapLiteral([[
I('cmpt'),
new ArrayLiteral([
[BINARY(I('info'), '.', BINARY(I(RESERVED_NAMES.renderOnly), '.', I('cmpt'))), true],
[L(tagName), false]
])
]]),
new MapLiteral([[
I('cmpt'),
new ArrayLiteral([[L(tagName), false]])
]])
)
return CONDITIONAL(I('renderOnly'), thenValue, L(false))
}

private compileSlotRenderer (content: ANode[]) {
const args = [DEF('parentCtx'), DEF('data')]
const body: Statement[] = []
Expand Down
6 changes: 4 additions & 2 deletions src/compilers/element-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
createIfStrictEqual, createIfNotNull, createDefaultValue, createHTMLLiteralAppend, createHTMLExpressionAppend, NULL,
L, I, ASSIGN, DEF, BINARY
} from '../ast/renderer-ast-util'
import { HelperCall, ArrayIncludes, Else, Foreach, If, MapLiteral } from '../ast/renderer-ast-dfn'
import { HelperCall, ArrayIncludes, Else, Foreach, If, MapLiteral, Statement } from '../ast/renderer-ast-dfn'
import { sanExpr, OutputType } from './san-expr-compiler'
import assert from 'assert'

Expand All @@ -39,7 +39,7 @@ export class ElementCompiler {
/**
* 编译元素标签头
*/
* tagStart (aNode: AElement, dynamicTagName?: string) {
* tagStart (aNode: AElement, dynamicTagName?: string, beforeEnd?: () => Generator<Statement, void, unknown>) {
const props = aNode.props
const bindDirective = aNode.directives.bind
const tagName = aNode.tagName!
Expand All @@ -61,6 +61,8 @@ export class ElementCompiler {
for (const prop of props) yield * this.compileProperty(tagName, prop, propsIndex)
if (bindDirective) yield * this.compileBindProperties(tagName, bindDirective)

if (beforeEnd) yield * beforeEnd()

// element end '>'
yield createHTMLLiteralAppend('>')
}
Expand Down
Loading

0 comments on commit d78c4c9

Please sign in to comment.