diff --git a/packages/runtime-vapor/__tests__/apiSetupHelpers.spec.ts b/packages/runtime-vapor/__tests__/apiSetupHelpers.spec.ts new file mode 100644 index 000000000..31b9cb391 --- /dev/null +++ b/packages/runtime-vapor/__tests__/apiSetupHelpers.spec.ts @@ -0,0 +1,87 @@ +import type { SetupContext } from '../src/component' +import { + createComponent, + defineComponent, + ref, + template, + useAttrs, + useSlots, +} from '../src' +import { makeRender } from './_utils' + +const define = makeRender<any>() + +describe('SFC <script setup> helpers', () => { + test.todo('should warn runtime usage', () => {}) + + test('useSlots / useAttrs (no args)', () => { + let slots: SetupContext['slots'] | undefined + let attrs: SetupContext['attrs'] | undefined + + const Comp = { + setup() { + slots = useSlots() + attrs = useAttrs() + }, + } + const count = ref(0) + const passedAttrs = { id: () => count.value } + const passedSlots = { + default: () => template('')(), + x: () => template('')(), + } + + const { render } = define({ + render: () => createComponent(Comp, passedAttrs, passedSlots), + }) + render() + + expect(typeof slots!.default).toBe('function') + expect(typeof slots!.x).toBe('function') + expect(attrs).toMatchObject({ id: 0 }) + + count.value++ + expect(attrs).toMatchObject({ id: 1 }) + }) + + test('useSlots / useAttrs (with args)', () => { + let slots: SetupContext['slots'] | undefined + let attrs: SetupContext['attrs'] | undefined + let ctx: SetupContext | undefined + const Comp = defineComponent({ + setup(_, _ctx) { + slots = useSlots() + attrs = useAttrs() + ctx = _ctx + }, + }) + const { render } = define({ render: () => createComponent(Comp) }) + render() + expect(slots).toBe(ctx!.slots) + expect(attrs).toBe(ctx!.attrs) + }) + + describe.todo('mergeDefaults', () => { + test.todo('object syntax', () => {}) + test.todo('array syntax', () => {}) + test.todo('merging with skipFactory', () => {}) + test.todo('should warn missing', () => {}) + }) + + describe('mergeModels', () => { + test.todo('array syntax', () => {}) + test.todo('object syntax', () => {}) + test.todo('overwrite', () => {}) + }) + + test.todo('createPropsRestProxy', () => {}) + + describe.todo('withAsyncContext', () => { + test.todo('basic', async () => {}) + test.todo('error handling', async () => {}) + test.todo('should not leak instance on multiple awaits', async () => {}) + test.todo('should not leak on multiple awaits + error', async () => {}) + test.todo('race conditions', async () => {}) + test.todo('should teardown in-scope effects', async () => {}) + }) +}) diff --git a/packages/runtime-vapor/src/apiSetupHelpers.ts b/packages/runtime-vapor/src/apiSetupHelpers.ts new file mode 100644 index 000000000..4afd43046 --- /dev/null +++ b/packages/runtime-vapor/src/apiSetupHelpers.ts @@ -0,0 +1,24 @@ +import { + type SetupContext, + createSetupContext, + getCurrentInstance, +} from './component' +import { warn } from './warning' + +// TODO: warning compiler-macros runtime usages + +export function useSlots(): SetupContext['slots'] { + return getContext().slots +} + +export function useAttrs(): SetupContext['attrs'] { + return getContext().attrs +} + +function getContext(): SetupContext { + const i = getCurrentInstance()! + if (__DEV__ && !i) { + warn(`useContext() called without active instance.`) + } + return i.setupContext || (i.setupContext = createSetupContext(i)) +} diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index cb8b28daf..b15f4c461 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -110,6 +110,7 @@ export { onErrorCaptured, // onServerPrefetch, } from './apiLifecycle' +export { useAttrs, useSlots } from './apiSetupHelpers' export { createVaporApp, type App, diff --git a/playground/src/sub-comp.vue b/playground/src/sub-comp.vue index 33e7ebd64..7b8ad36c0 100644 --- a/playground/src/sub-comp.vue +++ b/playground/src/sub-comp.vue @@ -1,10 +1,10 @@ <script setup lang="ts"> import { - getCurrentInstance, onBeforeMount, onBeforeUnmount, onMounted, onUnmounted, + useAttrs, watchEffect, } from 'vue/vapor' @@ -14,7 +14,7 @@ const props = defineProps<{ baz: string }>() -const attrs = getCurrentInstance()?.attrs +const attrs = useAttrs() watchEffect(() => { console.log({ ...attrs }) @@ -29,8 +29,12 @@ onUnmounted(() => console.log('sub: unmounted')) </script> <template> - <div>sub-comp</div> - {{ props }} - {{ attrs }} - {{ keys(attrs) }} + <h2>sub-comp</h2> + <p> + props: {{ props }} + <br /> + attrs: {{ attrs }} + <br /> + keys(attrs): {{ keys(attrs) }} + </p> </template>