Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,24 @@ export function render(_ctx) {
return n0
}"
`;

exports[`v-html > work with component 1`] = `
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, setBlockHtml as _setBlockHtml, renderEffect as _renderEffect } from 'vue';

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponentWithFallback(_component_Comp, null, null, true)
_renderEffect(() => _setBlockHtml(n0, _ctx.foo))
return n0
}"
`;

exports[`v-html > work with dynamic component 1`] = `
"import { createDynamicComponent as _createDynamicComponent, setBlockHtml as _setBlockHtml, renderEffect as _renderEffect } from 'vue';

export function render(_ctx) {
const n0 = _createDynamicComponent(() => (_ctx.Comp), null, null, true)
_renderEffect(() => _setBlockHtml(n0, _ctx.foo))
return n0
}"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,24 @@ export function render(_ctx) {
return n0
}"
`;

exports[`v-text > work with component 1`] = `
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, toDisplayString as _toDisplayString, setBlockText as _setBlockText, renderEffect as _renderEffect } from 'vue';

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponentWithFallback(_component_Comp, null, null, true)
_renderEffect(() => _setBlockText(n0, _toDisplayString(_ctx.foo)))
return n0
}"
`;

exports[`v-text > work with dynamic component 1`] = `
"import { createDynamicComponent as _createDynamicComponent, toDisplayString as _toDisplayString, setBlockText as _setBlockText, renderEffect as _renderEffect } from 'vue';

export function render(_ctx) {
const n0 = _createDynamicComponent(() => (_ctx.Comp), null, null, true)
_renderEffect(() => _setBlockText(n0, _toDisplayString(_ctx.foo)))
return n0
}"
`;
12 changes: 12 additions & 0 deletions packages/compiler-vapor/__tests__/transforms/vHtml.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ describe('v-html', () => {
expect(code).matchSnapshot()
})

test('work with dynamic component', () => {
const { code } = compileWithVHtml(`<component :is="Comp" v-html="foo"/>`)
expect(code).matchSnapshot()
expect(code).contains('setBlockHtml(n0, _ctx.foo))')
})

test('work with component', () => {
const { code } = compileWithVHtml(`<Comp v-html="foo"/>`)
expect(code).matchSnapshot()
expect(code).contains('setBlockHtml(n0, _ctx.foo))')
})

test('should raise error and ignore children when v-html is present', () => {
const onError = vi.fn()
const { code, ir, helpers } = compileWithVHtml(
Expand Down
12 changes: 12 additions & 0 deletions packages/compiler-vapor/__tests__/transforms/vText.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ describe('v-text', () => {
expect(code).matchSnapshot()
})

test('work with dynamic component', () => {
const { code } = compileWithVText(`<component :is="Comp" v-text="foo"/>`)
expect(code).matchSnapshot()
expect(code).contains('setBlockText(n0, _toDisplayString(_ctx.foo))')
})

test('work with component', () => {
const { code } = compileWithVText(`<Comp v-text="foo"/>`)
expect(code).matchSnapshot()
expect(code).contains('setBlockText(n0, _toDisplayString(_ctx.foo))')
})

test('should raise error and ignore children when v-text is present', () => {
const onError = vi.fn()
const { code, ir } = compileWithVText(`<div v-text="test">hello</div>`, {
Expand Down
10 changes: 8 additions & 2 deletions packages/compiler-vapor/src/generators/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ export function genSetHtml(
context: CodegenContext,
): CodeFragment[] {
const { helper } = context
const { value, element } = oper

const { value, element, isComponent } = oper
return [
NEWLINE,
...genCall(helper('setHtml'), `n${element}`, genExpression(value, context)),
...genCall(
// use setBlockHtml for component
isComponent ? helper('setBlockHtml') : helper('setHtml'),
`n${element}`,
genExpression(value, context),
),
]
}
9 changes: 7 additions & 2 deletions packages/compiler-vapor/src/generators/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ export function genSetText(
context: CodegenContext,
): CodeFragment[] {
const { helper } = context
const { element, values, generated, jsx } = oper
const { element, values, generated, jsx, isComponent } = oper
const texts = combineValues(values, context, jsx)
return [
NEWLINE,
...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts),
...genCall(
// use setBlockText for component
isComponent ? helper('setBlockText') : helper('setText'),
`${generated && !isComponent ? 'x' : 'n'}${element}`,
texts,
),
]
}

Expand Down
2 changes: 2 additions & 0 deletions packages/compiler-vapor/src/ir/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export interface SetTextIRNode extends BaseIRNode {
values: SimpleExpressionNode[]
generated?: boolean // whether this is a generated empty text node by `processTextLikeContainer`
jsx?: boolean
isComponent?: boolean
}

export type KeyOverride = [find: string, replacement: string]
Expand All @@ -150,6 +151,7 @@ export interface SetHtmlIRNode extends BaseIRNode {
type: IRNodeTypes.SET_HTML
element: number
value: SimpleExpressionNode
isComponent?: boolean
}

export interface SetTemplateRefIRNode extends BaseIRNode {
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/transforms/vHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ export const transformVHtml: DirectiveTransform = (dir, node, context) => {
type: IRNodeTypes.SET_HTML,
element: context.reference(),
value: exp,
isComponent: node.tagType === 1,
})
}
12 changes: 8 additions & 4 deletions packages/compiler-vapor/src/transforms/vText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ export const transformVText: DirectiveTransform = (dir, node, context) => {
context.childrenTemplate = [String(literal)]
} else {
context.childrenTemplate = [' ']
context.registerOperation({
type: IRNodeTypes.GET_TEXT_CHILD,
parent: context.reference(),
})
const isComponent = node.tagType === 1
if (!isComponent) {
context.registerOperation({
type: IRNodeTypes.GET_TEXT_CHILD,
parent: context.reference(),
})
}
context.registerEffect([exp], {
type: IRNodeTypes.SET_TEXT,
element: context.reference(),
values: [exp],
generated: true,
isComponent,
})
}
}
199 changes: 197 additions & 2 deletions packages/runtime-vapor/__tests__/dom/prop.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { NOOP } from '@vue/shared'
import { NOOP, toDisplayString } from '@vue/shared'
import {
setDynamicProp as _setDynamicProp,
setAttr,
setBlockHtml,
setBlockText,
setClass,
setDynamicProps,
setElementText,
Expand All @@ -11,8 +13,15 @@ import {
setValue,
} from '../../src/dom/prop'
import { setStyle } from '../../src/dom/prop'
import { VaporComponentInstance } from '../../src/component'
import { VaporComponentInstance, createComponent } from '../../src/component'
import { ref, setCurrentInstance } from '@vue/runtime-dom'
import { makeRender } from '../_utils'
import {
createDynamicComponent,
defineVaporComponent,
renderEffect,
template,
} from '../../src'

let removeComponentInstance = NOOP
beforeEach(() => {
Expand All @@ -24,6 +33,8 @@ afterEach(() => {
removeComponentInstance()
})

const define = makeRender()

describe('patchProp', () => {
describe('setClass', () => {
test('should set class', () => {
Expand Down Expand Up @@ -444,4 +455,188 @@ describe('patchProp', () => {
expect(el.innerHTML).toBe('<p>bar</p>')
})
})

describe('setBlockText', () => {
test('with dynamic component', async () => {
const Comp = defineVaporComponent({
setup() {
return template('<div>child</div>', true)()
},
})
const value = ref('foo')
const { html } = define({
setup() {
const n1 = createDynamicComponent(() => Comp, null, null, true)
renderEffect(() => setBlockText(n1, toDisplayString(value)))
return n1
},
}).render()

expect(html()).toBe('<div>foo</div><!--dynamic-component-->')
})

test('with dynamic component with fallback', async () => {
const value = ref('foo')
const { html } = define({
setup() {
const n1 = createDynamicComponent(() => 'button', null, null, true)
renderEffect(() => setBlockText(n1, toDisplayString(value)))
return n1
},
}).render()

expect(html()).toBe('<button>foo</button><!--dynamic-component-->')
})

test('with component', async () => {
const Comp = defineVaporComponent({
setup() {
return template('<div>child</div>', true)()
},
})
const value = ref('foo')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockText(n1, toDisplayString(value)))
return n1
},
}).render()

expect(html()).toBe('<div>foo</div>')
})

test('with component renders multiple roots nodes', async () => {
const Comp = defineVaporComponent({
setup() {
return [
template('<div>child</div>')(),
template('<div>child</div>')(),
]
},
})
const value = ref('foo')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockText(n1, toDisplayString(value)))
return n1
},
}).render()

expect(html()).toBe('<div>child</div><div>child</div>')
expect('Extraneous non-props attributes (textContent)').toHaveBeenWarned()
})

test('with component renders text node', async () => {
const Comp = defineVaporComponent({
setup() {
return template('child')()
},
})
const value = ref('foo')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockText(n1, toDisplayString(value)))
return n1
},
}).render()

expect(html()).toBe('child')
expect('Extraneous non-props attributes (textContent)').toHaveBeenWarned()
})
})

describe('setBlockHtml', () => {
test('with dynamic component', async () => {
const Comp = defineVaporComponent({
setup() {
return template('<div>child</div>', true)()
},
})
const value = ref('<p>foo</p>')
const { html } = define({
setup() {
const n1 = createDynamicComponent(() => Comp, null, null, true)
renderEffect(() => setBlockHtml(n1, value.value))
return n1
},
}).render()

expect(html()).toBe('<div><p>foo</p></div><!--dynamic-component-->')
})

test('with dynamic component with fallback', async () => {
const value = ref('<p>foo</p>')
const { html } = define({
setup() {
const n1 = createDynamicComponent(() => 'button', null, null, true)
renderEffect(() => setBlockHtml(n1, value.value))
return n1
},
}).render()

expect(html()).toBe('<button><p>foo</p></button><!--dynamic-component-->')
})

test('with component', async () => {
const Comp = defineVaporComponent({
setup() {
return template('<div>child</div>', true)()
},
})
const value = ref('<p>foo</p>')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockHtml(n1, value.value))
return n1
},
}).render()

expect(html()).toBe('<div><p>foo</p></div>')
})

test('with component renders multiple roots', async () => {
const Comp = defineVaporComponent({
setup() {
return [
template('<div>child</div>')(),
template('<div>child</div>')(),
]
},
})
const value = ref('<p>foo</p>')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockHtml(n1, value.value))
return n1
},
}).render()

expect(html()).toBe('<div>child</div><div>child</div>')
expect('Extraneous non-props attributes (innerHTML)').toHaveBeenWarned()
})

test('with component renders text node', async () => {
const Comp = defineVaporComponent({
setup() {
return template('child')()
},
})
const value = ref('<p>foo</p>')
const { html } = define({
setup() {
const n1 = createComponent(Comp, null, null, true)
renderEffect(() => setBlockHtml(n1, value.value))
return n1
},
}).render()

expect(html()).toBe('child')
expect('Extraneous non-props attributes (innerHTML)').toHaveBeenWarned()
})
})
})
Loading
Loading