Skip to content

Commit

Permalink
fix: useProvidedComponentClass 时,传入复杂类,会报错
Browse files Browse the repository at this point in the history
  • Loading branch information
meixg committed Dec 15, 2021
1 parent f08b821 commit d66d8a9
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 28 deletions.
12 changes: 10 additions & 2 deletions src/ast/renderer-ast-dfn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ export enum SyntaxKind {
Undefined = 35,
TryStatement = 36,
CatchClause = 37,
ComponentClassReference = 38
ComponentClassReference = 38,
CreateComponentPrototype = 39
}

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 | SlotRendererDefinition | SlotRenderCall | ComponentClassReference
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 | SlotRendererDefinition | SlotRenderCall | ComponentClassReference | CreateComponentPrototype

export type Statement = ReturnStatement | ImportHelper | VariableDefinition | AssignmentStatement | If | ElseIf | Else | Foreach | ExpressionStatement | TryStatement

Expand Down Expand Up @@ -173,6 +174,13 @@ export class CreateComponentInstance implements SyntaxNode {
) {}
}

export class CreateComponentPrototype implements SyntaxNode {
public readonly kind = SyntaxKind.CreateComponentPrototype
constructor (
public info: ComponentInfo
) {}
}

export class NewExpression implements SyntaxNode {
public readonly kind = SyntaxKind.NewExpression
constructor (
Expand Down
1 change: 1 addition & 0 deletions src/ast/renderer-ast-walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function * walk (node: Expression | Statement): Iterable<Expression | Sta
case SyntaxKind.Literal:
case SyntaxKind.Identifier:
case SyntaxKind.CreateComponentInstance:
case SyntaxKind.CreateComponentPrototype:
case SyntaxKind.Null:
case SyntaxKind.Undefined:
case SyntaxKind.ImportHelper:
Expand Down
17 changes: 6 additions & 11 deletions src/compilers/renderer-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,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 { FunctionDefinition, ComputedCall, Foreach, FunctionCall, MapLiteral, If, CreateComponentInstance, ImportHelper, ComponentReferenceLiteral, ConditionalExpression, BinaryExpression, CreateComponentPrototype } from '../ast/renderer-ast-dfn'
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 Down Expand Up @@ -52,14 +52,16 @@ export class RendererCompiler {
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'))))
}

// helper
body.push(new ImportHelper('_'))
body.push(new ImportHelper('SanSSRData'))

if (this.options.useProvidedComponentClass) {
body.push(DEF('ComponentClass', new BinaryExpression(I('info'), '.', I('ComponentClass'))))
body.push(STATEMENT(new CreateComponentPrototype(info)))
}

// context
body.push(this.compileGenInstance(info))
body.push(...this.compileContext(info))
Expand Down Expand Up @@ -102,13 +104,6 @@ export class RendererCompiler {
}

private compileGenInstance (info: ComponentInfo) {
if (this.options.useProvidedComponentClass) {
return DEF('instance', new FunctionCall(
BINARY(I('Object'), '.', I('create')),
[BINARY(I('ComponentClass'), '.', I('prototype'))]
))
}

return DEF('instance', new CreateComponentInstance(info))
}

Expand Down
20 changes: 10 additions & 10 deletions src/runtime/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function createResolver (exports: {[key: string]: any}, require: nodeRequ
}
return mod.sanSSRRenders[id]
},
getChildComponentClass: function ({ id, specifier = '.' }, CurrentComponentClass: Component, tagName: string, context) {
getChildComponentClass: function ({ id, specifier = '.' }, instance: Component, tagName: string, context) {
const customComponentFilePath = context && context.customComponentFilePath

if (customComponentFilePath && specifier !== '.') {
Expand All @@ -58,18 +58,18 @@ export function createResolver (exports: {[key: string]: any}, require: nodeRequ
else if (typeof path === 'function') return path
}

const components = CurrentComponentClass.prototype.components as {[tagName: string]: Component | undefined}
const ChildComponentClass = components[tagName]
if (!ChildComponentClass) {
throw Error(`child component is not fount: ${tagName}${CurrentComponentClass.prototype?.id || ''}`)
const components = instance.components || (instance.prototype && instance.prototype.components)
const ChildComponentClassOrInstance = components && components[tagName]
if (!ChildComponentClassOrInstance) {
throw Error(`child component is not fount: ${tagName}${instance.prototype?.id || ''}`)
}
if (typeof ChildComponentClass === 'string' && ChildComponentClass === 'self') {
return CurrentComponentClass
if (typeof ChildComponentClassOrInstance === 'string' && ChildComponentClassOrInstance === 'self') {
return instance
}
if (typeof ChildComponentClass !== 'function') {
throw Error(`external component is not provided: ${tagName}${CurrentComponentClass.prototype?.id || ''}`)
if (typeof ChildComponentClassOrInstance !== 'function' && typeof ChildComponentClassOrInstance !== 'object') {
throw Error(`external component is not provided: ${tagName}${instance.prototype?.id || ''}`)
}
return ChildComponentClass
return ChildComponentClassOrInstance
},
setRenderer: function (id: string, fn: Function) {
exports.sanSSRRenders = exports.sanSSRRenders || {}
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/underscore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ function createInstanceFromClass (Clazz: Component<{}> & ComponentDefineOptions)
// property
// template filters components computed trimWhitespace delimiters
const template = Clazz.template || Clazz.prototype.template
const components = Clazz.components || Clazz.prototype.components
delete Clazz.components
delete Clazz.prototype.components
const computed = Clazz.computed || Clazz.prototype.computed
Expand All @@ -154,6 +155,7 @@ function createInstanceFromClass (Clazz: Component<{}> & ComponentDefineOptions)
const instance = new Clazz()
if (inited) Clazz.prototype.inited = inited
if (initData) Clazz.prototype.initData = initData
if (components) Clazz.prototype.components = components
Clazz.prototype.template = template
if (computed) instance['computed'] = Clazz.prototype.computed = Clazz.computed = computed
return instance
Expand Down
18 changes: 17 additions & 1 deletion src/target-js/js-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ export class JSEmitter extends Emitter {
case SyntaxKind.CreateComponentInstance:
this.write(`_.createFromPrototype(sanSSRResolver.getPrototype("${node.info.id}"));`)
break
case SyntaxKind.CreateComponentPrototype:
this.write(`if (!sanSSRResolver.getPrototype("${node.info.id}")) {`)
this.indent()
this.nextLine('if (typeof ComponentClass === \'function\') {')
this.indent()
this.nextLine(`sanSSRResolver.setPrototype("${node.info.id}", _.createInstanceFromClass(ComponentClass));`)
this.unindent()
this.nextLine('}')
this.nextLine('else {')
this.indent()
this.nextLine(`sanSSRResolver.setPrototype("${node.info.id}", ComponentClass);`)
this.unindent()
this.nextLine('}')
this.unindent()
this.nextLine('}')
break
case SyntaxKind.Null:
this.write('null')
break
Expand Down Expand Up @@ -131,7 +147,7 @@ export class JSEmitter extends Emitter {
case SyntaxKind.ComponentClassReference:
this.write('sanSSRResolver.getChildComponentClass(')
this.writeSyntaxNode(node.value)
this.write(', ComponentClass')
this.write(', instance')
this.write(', ')
this.writeSyntaxNode(node.tagName)
this.write(', ctx.context')
Expand Down
9 changes: 5 additions & 4 deletions test/unit/runtime/resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ describe('runtime/resolver', () => {
})
const MyComponent = san.defineComponent({
template: '',
// @ts-ignore
components: {
'child-a': ChildA,
'child-b': ChildA,
'child-c': 'self',
'child-d': {}
'child-d': 123
}
})
const ref = { id: 'id', specifier: './som/path' }
Expand Down Expand Up @@ -88,17 +89,17 @@ describe('runtime/resolver', () => {
} catch {
fn()
}
expect(fn).toHaveBeenCalled()
expect(fn.mock.calls.length).toBe(1)
})

it('should error if child component is not class', () => {
it('should error if child component is not class or object', () => {
const fn = jest.fn()
try {
resolver.getChildComponentClass(ref, MyComponent, 'child-d')
} catch {
fn()
}
expect(fn).toHaveBeenCalled()
expect(fn.mock.calls.length).toBe(1)
})
})
})
12 changes: 12 additions & 0 deletions test/unit/runtime/underscore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ describe('utils/underscore', function () {
expect(_.createInstanceFromClass(MyComponent)).toHaveProperty('computed.foo', foo)
expect(foo).not.toHaveBeenCalled()
})
it('should keep components', () => {
class AAA extends Component {
static template = '<div></div>'
}
class MyComponent extends Component {
static template = '<div><aaa/></div>'
static components = {
aaa: AAA
}
}
expect(_.createInstanceFromClass(MyComponent)).toHaveProperty('components.aaa', AAA)
})
})
describe('.attrFilter', () => {
it('should not escape if specified', () => {
Expand Down
1 change: 1 addition & 0 deletions types/san.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ declare namespace san {
el?: Element;
data: Data<T>;
parentComponent?: Component<{}>;
components?: ComponentDefineOptionComponents;

nodeType: NodeType.CMPT;
lifeCycle: LifeCycleStage;
Expand Down

0 comments on commit d66d8a9

Please sign in to comment.