Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render only component #165

Merged
merged 15 commits into from
Apr 17, 2023
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