Skip to content

Commit

Permalink
test: cover all lines and branches, 98.75%->100%
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed Mar 19, 2020
1 parent 440e48f commit a029f85
Show file tree
Hide file tree
Showing 21 changed files with 360 additions and 324 deletions.
4 changes: 2 additions & 2 deletions bin/debug
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function debugCompileToSource () {
const fileName = `${__dirname}/../test/cases/${caseName}/ssr.js`
writeFileSync(fileName, targetCode)

got = execFileSync(resolve(__dirname, `./render-by-source.js`), [caseName]).toString()
got = execFileSync(resolve(__dirname, `./render-by-source.js`), [caseName], { encoding: 'utf8' }).toString()
assertSanHTMLEqual(got, expected)
console.log(chalk.green(`[SOURCE] ${caseName}`), got)
} catch (err) {
Expand All @@ -45,7 +45,7 @@ function debugCompileToSource () {
function debugCompileToRenderer () {
let got
try {
got = execFileSync(resolve(__dirname, `./render-onthefly.js`), [caseName]).toString()
got = execFileSync(resolve(__dirname, `./render-onthefly.js`), [caseName], { encoding: 'utf8' }).toString()
assertSanHTMLEqual(got, expected)
console.log(chalk.green(`[RENDER] ${caseName}`), got)
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/models/component-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class ComponentTree {
const info = this.parser.parseComponent(ComponentClass)
this.nodes.set(ComponentClass, info)

for (let childComponentClass of Object.values(info.childComponentClasses)) {
for (let childComponentClass of info.childComponentClasses.values()) {
if (isComponentLoader(childComponentClass)) {
childComponentClass = childComponentClass.placeholder
}
Expand Down
11 changes: 2 additions & 9 deletions src/models/component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SanComponent, ComponentConstructor } from 'san'
import { ANode, ComponentConstructor } from 'san'
import { CompiledComponent } from './compiled-component'
import { SanData } from './san-data'

Expand All @@ -12,9 +12,7 @@ export interface Filters {

export type ComponentClass = ComponentConstructor<{}, {}>

export interface Components {
[key: string]: ComponentClass | { load: any, placeholder: ComponentClass }
}
export type Components = Map<string | ANode, ComponentClass | { load: any, placeholder: ComponentClass }>

export const COMPONENT_RESERVED_MEMBERS = new Set(
'aNode,computed,filters,components,' +
Expand All @@ -26,11 +24,6 @@ export function isComponentLoader (cmpt: any): cmpt is {placeholder: ComponentCl
return cmpt && cmpt.hasOwnProperty('load') && cmpt.hasOwnProperty('placeholder')
}

export function isComponentClass (clazz: any): clazz is typeof SanComponent {
return typeof clazz === 'function' &&
(typeof clazz.template === 'string' || typeof clazz.prototype.template === 'string')
}

export class SanSSRFiltersDeclarations {
[key: string]: (...args: any[]) => any
}
Expand Down
10 changes: 5 additions & 5 deletions src/models/san-project.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component as SanComponent } from 'san'
import { ComponentConstructor } from 'san'
import { Project } from 'ts-morph'
import { ToJSCompileOptions } from '../target-js/index'
import { TSSanAppParser } from '../parsers/ts-san-app-parser'
Expand Down Expand Up @@ -46,14 +46,14 @@ export class SanProject {
* @alias SanProject.compileToSource
*/
public compile (
filepathOrComponentClass: string | typeof SanComponent,
filepathOrComponentClass: string | ComponentConstructor<{}, any>,
target: string | CompilerClass = 'js',
options: CompileOptions = {}
) {
return this.compileToSource(filepathOrComponentClass, target, options)
}
public compileToSource (
filepathOrComponentClass: string | typeof SanComponent,
filepathOrComponentClass: string | ComponentConstructor<{}, any>,
target: string | CompilerClass = 'js',
options: CompileOptions = {}
) {
Expand All @@ -62,7 +62,7 @@ export class SanProject {
return compiler.compile(sanApp, options)
}
public parseSanApp (
filepathOrComponentClass: string | typeof SanComponent
filepathOrComponentClass: string | ComponentConstructor<{}, any>
) {
const parser = this.getParser()
const sanApp = typeof filepathOrComponentClass === 'string'
Expand All @@ -78,7 +78,7 @@ export class SanProject {
* * `options.bareFunction` is fixed to true
*/
public compileToRenderer (
filepathOrComponentClass: string | typeof SanComponent,
filepathOrComponentClass: string | ComponentConstructor<{}, any>,
options?: ToJSCompileOptions
): Renderer {
const sanApp = this.parseSanApp(filepathOrComponentClass)
Expand Down
133 changes: 66 additions & 67 deletions src/target-js/compilers/anode-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { stringLiteralize, expr } from './expr-compiler'
import { CompiledComponent } from '../../models/compiled-component'
import { isComponentLoader } from '../../models/component'
import { ComponentTree } from '../../models/component-tree'
import { JSEmitter } from '../emitters/emitter'
import { ANode, ComponentConstructor, ExprStringNode, AIfNode, AForNode, ASlotNode, ATemplateNode, ATextNode } from 'san'
import { ANode, ExprStringNode, AIfNode, AForNode, ASlotNode, ATemplateNode, ATextNode } from 'san'
import { ComponentInfo } from '../../models/component-info'
import { ElementCompiler } from './element-compiler'
import { stringifier } from './stringifier'
Expand All @@ -16,28 +16,32 @@ export class ANodeCompiler {
private ssrIndex = 0

constructor (
public component: CompiledComponent<{}>,
private componentInfo: ComponentInfo,
private componentTree: ComponentTree,
private elementSourceCompiler: ElementCompiler,
private getComponentInfoByClass: (ComponentClass: ComponentConstructor<{}, {}>) => ComponentInfo,
private emitter: JSEmitter
) {}
public emitter: JSEmitter
) {
}

compile (aNode: ANode) {
if (TypeGuards.isATextNode(aNode)) return this.compileText(aNode)
if (TypeGuards.isAIfNode(aNode)) return this.compileIf(aNode)
if (TypeGuards.isAForNode(aNode)) return this.compileFor(aNode)
if (TypeGuards.isASlotNode(aNode)) return this.compileSlot(aNode)
if (TypeGuards.isATemplateNode(aNode)) return this.compileTemplate(aNode)
const component = this.componentInfo.component

let ComponentClass = this.component.getComponentType
? this.component.getComponentType(aNode)
: this.component.components[aNode.tagName]
let ComponentClass = component.getComponentType
? component.getComponentType(aNode)
: component.components[aNode.tagName]
if (ComponentClass) {
if (isComponentLoader(ComponentClass)) {
ComponentClass = ComponentClass.placeholder
if (!ComponentClass) return // output nothing if placeholder undefined
}
const info = this.getComponentInfoByClass(ComponentClass)

// TODO 从编译时移到运行时,见:https://github.com/baidu/san-ssr/issues/46
const info = this.componentTree.addComponentClass(ComponentClass)
return this.compileComponent(aNode, info)
}
return this.compileElement(aNode)
Expand Down Expand Up @@ -143,7 +147,7 @@ export class ANodeCompiler {
emitter.nextLine('')
emitter.writeFunction('$defaultSlotRender', ['ctx'], () => {
emitter.writeLine('var html = "";')
for (const aNodeChild of aNode.children || []) {
for (const aNodeChild of aNode.children) {
this.compile(aNodeChild)
}
emitter.writeLine('return html;')
Expand Down Expand Up @@ -175,12 +179,12 @@ export class ANodeCompiler {

if (aNode.vars || aNode.directives.bind) {
emitter.writeLine('$slotCtx = {data: _.extend({}, $slotCtx.data), instance: $slotCtx.instance, owner: $slotCtx.owner};')

if (aNode.directives.bind) {
emitter.writeLine('_.extend($slotCtx.data, ' + expr(aNode.directives.bind.value) + ');')
}

for (const varItem of aNode.vars || []) {
}
if (aNode.directives.bind) {
emitter.writeLine('_.extend($slotCtx.data, ' + expr(aNode.directives.bind.value) + ');')
}
if (aNode.vars) {
for (const varItem of aNode.vars) {
emitter.writeLine(
'$slotCtx.data["' + varItem.name + '"] = ' +
expr(varItem.expr) +
Expand All @@ -207,69 +211,64 @@ export class ANodeCompiler {
private compileComponent (aNode: ANode, info: ComponentInfo) {
const { emitter } = this

emitter.writeLine('var $sourceSlots = [];')
if (aNode.children) {
const defaultSourceSlots: ANode[] = []
const sourceSlotCodes = {}

for (const child of aNode.children) {
const slotBind = !child.textExpr && getANodePropByName(child, 'slot')
if (slotBind) {
if (!sourceSlotCodes[slotBind.raw]) {
sourceSlotCodes[slotBind.raw] = {
children: [],
prop: slotBind
}
}

sourceSlotCodes[slotBind.raw].children.push(child)
} else {
defaultSourceSlots.push(child)
const defaultSourceSlots: ANode[] = []
const sourceSlotCodes = new Map()

for (const child of aNode.children!) { // nodes without children (like pATextNode) has been taken over by other methods
const slotBind = !child.textExpr && getANodePropByName(child, 'slot')
if (slotBind) {
if (!sourceSlotCodes.has(slotBind.raw)) {
sourceSlotCodes.set(slotBind.raw, {
children: [],
prop: slotBind
})
}
}

if (defaultSourceSlots.length) {
emitter.writeLine('$sourceSlots.push([function (ctx) {')
emitter.indent()
emitter.writeLine('var html = "";')
for (const child of defaultSourceSlots) this.compile(child)
emitter.writeLine('return html;')
emitter.unindent()
emitter.writeLine('}]);')
}

for (const key in sourceSlotCodes) {
const sourceSlotCode = sourceSlotCodes[key]
emitter.writeLine('$sourceSlots.push([function (ctx) {')
emitter.indent()
emitter.writeLine('var html = "";')
sourceSlotCode.children.forEach((child: ANode) => {
this.compile(child)
})
emitter.writeLine('return html;')
emitter.unindent()
emitter.writeLine('}, ' + expr(sourceSlotCode.prop.expr) + ']);')
sourceSlotCodes.get(slotBind.raw).children.push(child)
} else {
defaultSourceSlots.push(child)
}
}

const givenData = getANodeProps(aNode).map(prop => {
const key = stringLiteralize(prop.name)
const val = expr(prop.expr)
return `${key}: ${val}`
})
emitter.writeLine('var $sourceSlots = [];')
if (defaultSourceSlots.length) {
emitter.writeLine('$sourceSlots.push([')
this.compileSlotRenderer(defaultSourceSlots)
emitter.writeLine(']);')
}

let dataLiteral = '{' + givenData.join(',\n') + '}'
if (aNode.directives.bind) {
dataLiteral = `_.extend(${expr(aNode.directives.bind.value)}, ${dataLiteral})`
for (const sourceSlotCode of sourceSlotCodes.values()) {
emitter.writeLine('$sourceSlots.push([')
this.compileSlotRenderer(sourceSlotCode.children)
emitter.writeLine(', ' + expr(sourceSlotCode.prop.expr) + ']);')
}

const funcName = 'sanssrRuntime.renderer' + info.cid
emitter.nextLine(`html += ${funcName}(`)
emitter.write(dataLiteral + ', true, sanssrRuntime, ctx, ' +
emitter.write(this.componentDataCode(aNode) + ', true, sanssrRuntime, ctx, ' +
stringifier.str(aNode.tagName) + ', $sourceSlots);')
emitter.writeLine('$sourceSlots = null;')
}

private compileSlotRenderer (slots: ANode[]) {
const { emitter } = this
emitter.writeAnonymousFunction(['ctx'], () => {
emitter.writeLine('var html = "";')
for (const slot of slots) this.compile(slot)
emitter.writeLine('return html;')
})
}

private componentDataCode (aNode: ANode) {
const givenData = '{' + getANodeProps(aNode).map(prop => {
const key = stringLiteralize(prop.name)
const val = expr(prop.expr)
return `${key}: ${val}`
}).join(', ') + '}'

const bindDirective = aNode.directives.bind
return bindDirective ? `_.extend(${expr(bindDirective.value)}, ${givenData})` : givenData
}

private nextID () {
return 'sanssrId' + (this.ssrIndex++)
}
Expand Down

0 comments on commit a029f85

Please sign in to comment.