From 48fc65f25c22a13b5b0647c8e4be9a186e0f7396 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 10 Dec 2024 12:49:47 +0800 Subject: [PATCH] test(vapor): apiCreateVaporApp --- packages/runtime-core/src/component.ts | 3 + packages/runtime-core/src/index.ts | 2 + packages/runtime-core/src/profiling.ts | 15 +-- packages/runtime-core/src/warning.ts | 2 +- .../__tests__/apiCreateSelector.spec.ts | 9 +- .../__tests__/apiCreateVaporApp.spec.ts | 97 ++++++++++--------- .../runtime-vapor/__tests__/apiExpose.spec.ts | 15 +-- .../__tests__/dom/templateRef.spec.ts | 21 ++-- packages/runtime-vapor/src/apiCreateApp.ts | 22 ++++- packages/runtime-vapor/src/component.ts | 35 +++++-- packages/runtime-vapor/src/renderEffect.ts | 9 +- 11 files changed, 154 insertions(+), 76 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 72a595258..db717f84e 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -785,6 +785,9 @@ export function createComponentInstance( return instance } +/** + * @internal + */ export function validateComponentName( name: string, { isNativeTag }: AppConfig, diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 00bc68573..0a0313e1d 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -500,6 +500,7 @@ export { type GenericComponentInstance, type LifecycleHook, nextUid, + validateComponentName, } from './component' export { pushWarningContext, popWarningContext } from './warning' export { @@ -512,3 +513,4 @@ export { simpleSetCurrentInstance, } from './componentCurrentInstance' export { registerHMR, unregisterHMR } from './hmr' +export { startMeasure, endMeasure } from './profiling' diff --git a/packages/runtime-core/src/profiling.ts b/packages/runtime-core/src/profiling.ts index 1984f5a21..ec789de72 100644 --- a/packages/runtime-core/src/profiling.ts +++ b/packages/runtime-core/src/profiling.ts @@ -1,15 +1,15 @@ /* eslint-disable no-restricted-globals */ -import { - type ComponentInternalInstance, - formatComponentName, -} from './component' +import { type GenericComponentInstance, formatComponentName } from './component' import { devtoolsPerfEnd, devtoolsPerfStart } from './devtools' let supported: boolean let perf: Performance +/** + * @internal + */ export function startMeasure( - instance: ComponentInternalInstance, + instance: GenericComponentInstance, type: string, ): void { if (instance.appContext.config.performance && isSupported()) { @@ -21,8 +21,11 @@ export function startMeasure( } } +/** + * @internal + */ export function endMeasure( - instance: ComponentInternalInstance, + instance: GenericComponentInstance, type: string, ): void { if (instance.appContext.config.performance && isSupported()) { diff --git a/packages/runtime-core/src/warning.ts b/packages/runtime-core/src/warning.ts index 222a576c2..c261bf602 100644 --- a/packages/runtime-core/src/warning.ts +++ b/packages/runtime-core/src/warning.ts @@ -57,7 +57,7 @@ export function warn(msg: string, ...args: any[]): void { [ // eslint-disable-next-line no-restricted-syntax msg + args.map(a => a.toString?.() ?? JSON.stringify(a)).join(''), - instance && instance.proxy, + (instance && instance.proxy) || instance, trace .map( ({ ctx }) => diff --git a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts index 87dbf5aba..05f95bf08 100644 --- a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts @@ -1,6 +1,8 @@ import { ref } from '@vue/reactivity' import { makeRender } from './_utils' -// import { createFor, createSelector, nextTick, renderEffect } from '../src' +// @ts-expect-error +import { createFor, createSelector, renderEffect } from '../src' +import { nextTick } from '@vue/runtime-dom' const define = makeRender() @@ -16,6 +18,7 @@ describe.todo('api: createSelector', () => { const isSleected = createSelector(index) return createFor( () => list.value, + // @ts-expect-error ([item]) => { const span = document.createElement('li') renderEffect(() => { @@ -25,6 +28,7 @@ describe.todo('api: createSelector', () => { }) return span }, + // @ts-expect-error item => item.id, ) }).render() @@ -66,10 +70,12 @@ describe.todo('api: createSelector', () => { const { host } = define(() => { const isSleected = createSelector( index, + // @ts-expect-error (key, value) => key === value + 1, ) return createFor( () => list.value, + // @ts-expect-error ([item]) => { const span = document.createElement('li') renderEffect(() => { @@ -79,6 +85,7 @@ describe.todo('api: createSelector', () => { }) return span }, + // @ts-expect-error item => item.id, ) }).render() diff --git a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts index faa760675..a8bbd96a9 100644 --- a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts @@ -1,25 +1,29 @@ import { - type ComponentInternalInstance, - type Plugin, createComponent, createTextNode, createVaporApp, - defineComponent, - getCurrentInstance, + defineVaporComponent, + // @ts-expect-error + withDirectives, +} from '../src' +import { + type GenericComponentInstance, + type Plugin, + currentInstance, inject, provide, resolveComponent, resolveDirective, - withDirectives, -} from '../src' -import { warn } from '@vue/runtime-dom' + warn, +} from '@vue/runtime-dom' import { makeRender } from './_utils' +import type { VaporComponent } from '../src/component' const define = makeRender() -describe.todo('api: createVaporApp', () => { +describe('api: createVaporApp', () => { test('mount', () => { - const Comp = defineComponent({ + const Comp = defineVaporComponent({ props: { count: { default: 0 }, }, @@ -51,7 +55,7 @@ describe.todo('api: createVaporApp', () => { }) test('unmount', () => { - const Comp = defineComponent({ + const Comp = defineVaporComponent({ props: { count: { default: 0 }, }, @@ -82,22 +86,22 @@ describe.todo('api: createVaporApp', () => { }, }) - const Child = defineComponent({ + const Child = defineVaporComponent({ setup() { const foo = inject('foo') const bar = inject('bar') try { inject('__proto__') } catch (e: any) {} - return createTextNode(() => [`${foo},${bar}`]) + return createTextNode([`${foo},${bar}`]) }, }) - const { app, mount, create, host } = Root.create(null) + const { app, mount, create, html } = Root.create() app.provide('foo', 1) app.provide('bar', 2) mount() - expect(host.innerHTML).toBe(`3,2`) + expect(html()).toBe(`3,2`) expect('[Vue warn]: injection "__proto__" not found.').toHaveBeenWarned() const { app: app2 } = create() @@ -132,8 +136,8 @@ describe.todo('api: createVaporApp', () => { test('component', () => { const { app, mount, host } = define({ setup() { - const FooBar = resolveComponent('foo-bar') - const BarBaz = resolveComponent('bar-baz') + const FooBar = resolveComponent('foo-bar') as VaporComponent + const BarBaz = resolveComponent('bar-baz') as VaporComponent return [createComponent(FooBar), createComponent(BarBaz)] }, }).create() @@ -152,7 +156,7 @@ describe.todo('api: createVaporApp', () => { expect(host.innerHTML).toBe(`foobar!barbaz!`) }) - test('directive', () => { + test.todo('directive', () => { const spy1 = vi.fn() const spy2 = vi.fn() @@ -229,7 +233,7 @@ describe.todo('api: createVaporApp', () => { test('config.errorHandler', () => { const error = new Error() - let instance: ComponentInternalInstance + let instance: GenericComponentInstance const handler = vi.fn((err, _instance, info) => { expect(err).toBe(error) @@ -239,7 +243,8 @@ describe.todo('api: createVaporApp', () => { const { app, mount } = define({ setup() { - instance = getCurrentInstance()! + instance = currentInstance! + return {} }, render() { throw error @@ -251,7 +256,7 @@ describe.todo('api: createVaporApp', () => { }) test('config.warnHandler', () => { - let instance: ComponentInternalInstance + let instance: GenericComponentInstance const handler = vi.fn((msg, _instance, trace) => { expect(msg).toMatch(`warn message`) @@ -262,8 +267,9 @@ describe.todo('api: createVaporApp', () => { const { app, mount } = define({ name: 'Hello', setup() { - instance = getCurrentInstance()! + instance = currentInstance! warn('warn message') + return [] }, }).create() @@ -275,22 +281,23 @@ describe.todo('api: createVaporApp', () => { describe('config.isNativeTag', () => { const isNativeTag = vi.fn(tag => tag === 'div') - test('Component.name', () => { - const { app, mount } = define({ - name: 'div', - render(): any {}, - }).create() + // Not relevant for vapor + // test('Component.name', () => { + // const { app, mount } = define({ + // name: 'div', + // render(): any {}, + // }).create() - Object.defineProperty(app.config, 'isNativeTag', { - value: isNativeTag, - writable: false, - }) + // Object.defineProperty(app.config, 'isNativeTag', { + // value: isNativeTag, + // writable: false, + // }) - mount() - expect( - `Do not use built-in or reserved HTML elements as component id: div`, - ).toHaveBeenWarned() - }) + // mount() + // expect( + // `Do not use built-in or reserved HTML elements as component id: div`, + // ).toHaveBeenWarned() + // }) test('register using app.component', () => { const { app, mount } = define({ @@ -316,7 +323,7 @@ describe.todo('api: createVaporApp', () => { }) test('with performance enabled', () => { - const { app, mount } = define({}).create() + const { app, mount } = define({ setup: () => [] }).create() app.config.performance = true mount() @@ -324,7 +331,7 @@ describe.todo('api: createVaporApp', () => { }) test('with performance disabled', () => { - const { app, mount } = define({}).create() + const { app, mount } = define({ setup: () => [] }).create() app.config.performance = false mount() @@ -333,14 +340,16 @@ describe.todo('api: createVaporApp', () => { }) test('config.globalProperty', () => { - const { app, mount, html } = define({ - render() { - const instance = getCurrentInstance()! - return createTextNode([instance.appContext.config.globalProperties.msg]) + const { app } = define({ + setup() { + return [] }, }).create() - app.config.globalProperties.msg = 'hello world' - mount() - expect(html()).toBe('hello world') + try { + app.config.globalProperties.msg = 'hello world' + } catch (e) {} + expect( + `app.config.globalProperties is not supported in vapor mode`, + ).toHaveBeenWarned() }) }) diff --git a/packages/runtime-vapor/__tests__/apiExpose.spec.ts b/packages/runtime-vapor/__tests__/apiExpose.spec.ts index e5b482c5f..91f2b76af 100644 --- a/packages/runtime-vapor/__tests__/apiExpose.spec.ts +++ b/packages/runtime-vapor/__tests__/apiExpose.spec.ts @@ -1,11 +1,8 @@ import { ref, shallowRef } from '@vue/reactivity' -import { createComponent } from '../src/component' +import { type VaporComponentInstance, createComponent } from '../src/component' import { setRef } from '../src/dom/templateRef' import { makeRender } from './_utils' -import { - type ComponentInternalInstance, - getCurrentInstance, -} from '../src/component' +import { currentInstance } from '@vue/runtime-dom' import { defineVaporComponent } from '../src/apiDefineComponent' const define = makeRender() @@ -39,10 +36,11 @@ describe.todo('api: expose', () => { }) test('via setup context (expose empty)', () => { - let childInstance: ComponentInternalInstance | null = null + let childInstance: VaporComponentInstance | null = null const Child = defineVaporComponent({ setup(_) { - childInstance = getCurrentInstance() + childInstance = currentInstance as VaporComponentInstance + return [] }, }) const childRef = shallowRef() @@ -77,6 +75,7 @@ describe.todo('api: expose', () => { define({ setup(_, { expose }) { expose(ref(1)) + return [] }, }).render() @@ -89,6 +88,7 @@ describe.todo('api: expose', () => { define({ setup(_, { expose }) { expose(['focus']) + return [] }, }).render() @@ -101,6 +101,7 @@ describe.todo('api: expose', () => { define({ setup(_, { expose }) { expose(() => null) + return [] }, }).render() diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts index 16bd6403c..f4fb1d077 100644 --- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts @@ -1,19 +1,23 @@ import type { NodeRef } from '../../src/dom/templateRef' import { + // @ts-expect-error createFor, + // @ts-expect-error createIf, - getCurrentInstance, insert, - nextTick, - reactive, - ref, renderEffect, setRef, setText, template, - watchEffect, } from '../../src' import { makeRender } from '../_utils' +import { + currentInstance, + nextTick, + reactive, + ref, + watchEffect, +} from '@vue/runtime-dom' const define = makeRender() @@ -257,7 +261,7 @@ describe.todo('api: template ref', () => { const t1 = template('') const { render } = define({ render() { - const instance = getCurrentInstance()! + const instance = currentInstance! const n0 = t0() const n1 = t1() let r0: NodeRef | undefined @@ -303,7 +307,7 @@ describe.todo('api: template ref', () => { const t1 = template('') const { render } = define({ render() { - const instance = getCurrentInstance()! + const instance = currentInstance! const n0 = createIf( () => refToggle.value, () => { @@ -355,6 +359,7 @@ describe.todo('api: template ref', () => { const n1 = t0() const n2 = createFor( () => list, + // @ts-expect-error state => { const n1 = t1() setRef(n1 as Element, listRefs, undefined, true) @@ -413,6 +418,7 @@ describe.todo('api: template ref', () => { const n1 = t0() const n2 = createFor( () => list, + // @ts-expect-error state => { const n1 = t1() setRef(n1 as Element, 'listRefs', undefined, true) @@ -469,6 +475,7 @@ describe.todo('api: template ref', () => { const n2 = n1!.nextSibling! const n3 = createFor( () => list.value, + // @ts-expect-error state => { const n4 = t1() setRef(n4 as Element, 'listRefs', undefined, true) diff --git a/packages/runtime-vapor/src/apiCreateApp.ts b/packages/runtime-vapor/src/apiCreateApp.ts index fc976f7b5..7c4db8961 100644 --- a/packages/runtime-vapor/src/apiCreateApp.ts +++ b/packages/runtime-vapor/src/apiCreateApp.ts @@ -11,6 +11,7 @@ import { type CreateAppFunction, createAppAPI, normalizeContainer, + warn, } from '@vue/runtime-dom' import type { RawProps } from './componentProps' @@ -21,7 +22,13 @@ const mountApp: AppMountFn = (app, container) => { if (container.nodeType === 1 /* Node.ELEMENT_NODE */) { container.textContent = '' } - const instance = createComponent(app._component, app._props as RawProps) + const instance = createComponent( + app._component, + app._props as RawProps, + null, + false, + app._context, + ) mountComponent(instance, container) return instance } @@ -36,6 +43,19 @@ export const createVaporApp: CreateAppFunction = ( ) => { if (!_createApp) _createApp = createAppAPI(mountApp, unmountApp, i => i) const app = _createApp(comp, props) + + if (__DEV__) { + app.config.globalProperties = new Proxy( + {}, + { + set() { + warn(`app.config.globalProperties is not supported in vapor mode.`) + return false + }, + }, + ) + } + const mount = app.mount app.mount = (container, ...args: any[]) => { container = normalizeContainer(container) as ParentNode diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 033b2fa6a..f544809b7 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -13,11 +13,13 @@ import { type SuspenseBoundary, callWithErrorHandling, currentInstance, + endMeasure, nextUid, popWarningContext, pushWarningContext, registerHMR, simpleSetCurrentInstance, + startMeasure, unregisterHMR, warn, } from '@vue/runtime-dom' @@ -100,6 +102,7 @@ export function createComponent( rawProps?: RawProps | null, rawSlots?: RawSlots | null, isSingleRoot?: boolean, + appContext?: GenericAppContext, ): VaporComponentInstance { // check if we are the single root of the parent // if yes, inject parent attrs as dynamic props source @@ -117,15 +120,22 @@ export function createComponent( } } - const instance = new VaporComponentInstance(component, rawProps, rawSlots) - const prev = currentInstance - simpleSetCurrentInstance(instance) + const instance = new VaporComponentInstance( + component, + rawProps, + rawSlots, + appContext, + ) - pauseTracking() if (__DEV__) { pushWarningContext(instance) + startMeasure(instance, `init`) } + const prev = currentInstance + simpleSetCurrentInstance(instance) + pauseTracking() + const setupFn = isFunction(component) ? component : component.setup const setupContext = (instance.setupContext = setupFn && setupFn.length > 1 ? new SetupContext(instance) : null) @@ -177,12 +187,14 @@ export function createComponent( }) } - if (__DEV__) { - popWarningContext() - } resetTracking() simpleSetCurrentInstance(prev, instance) + if (__DEV__) { + popWarningContext() + endMeasure(instance, 'init') + } + return instance } @@ -280,6 +292,7 @@ export class VaporComponentInstance implements GenericComponentInstance { comp: VaporComponent, rawProps?: RawProps | null, rawSlots?: RawSlots | null, + appContext?: GenericAppContext, ) { this.vapor = true this.uid = nextUid() @@ -295,7 +308,7 @@ export class VaporComponentInstance implements GenericComponentInstance { this.provides = currentInstance.provides this.ids = currentInstance.ids } else { - this.appContext = emptyContext + this.appContext = appContext || emptyContext this.provides = Object.create(this.appContext.provides) this.ids = ['', 0, 0] } @@ -417,6 +430,9 @@ export function mountComponent( parent: ParentNode, anchor?: Node | null | 0, ): void { + if (__DEV__) { + startMeasure(instance, `mount`) + } if (!instance.isMounted) { if (instance.bm) invokeArrayFns(instance.bm) insert(instance.block, parent, anchor) @@ -427,6 +443,9 @@ export function mountComponent( } else { insert(instance.block, parent, anchor) } + if (__DEV__) { + endMeasure(instance, `mount`) + } } export function unmountComponent( diff --git a/packages/runtime-vapor/src/renderEffect.ts b/packages/runtime-vapor/src/renderEffect.ts index 6dec684b0..b3d10b758 100644 --- a/packages/runtime-vapor/src/renderEffect.ts +++ b/packages/runtime-vapor/src/renderEffect.ts @@ -5,13 +5,14 @@ import { queueJob, queuePostFlushCb, simpleSetCurrentInstance, + startMeasure, warn, } from '@vue/runtime-dom' import { type VaporComponentInstance, isVaporComponent } from './component' import { invokeArrayFns } from '@vue/shared' export function renderEffect(fn: () => void, noLifecycle = false): void { - const instance = currentInstance as VaporComponentInstance + const instance = currentInstance as VaporComponentInstance | null const scope = getCurrentScope() if (__DEV__ && !__TEST__ && !isVaporComponent(instance)) { warn('renderEffect called without active vapor instance.') @@ -20,6 +21,9 @@ export function renderEffect(fn: () => void, noLifecycle = false): void { const renderEffectFn = noLifecycle ? fn : () => { + if (__DEV__ && instance) { + startMeasure(instance, `renderEffect`) + } const prev = currentInstance simpleSetCurrentInstance(instance) if (scope) scope.on() @@ -41,6 +45,9 @@ export function renderEffect(fn: () => void, noLifecycle = false): void { } if (scope) scope.off() simpleSetCurrentInstance(prev, instance) + if (__DEV__ && instance) { + startMeasure(instance, `renderEffect`) + } } const effect = new ReactiveEffect(renderEffectFn)