Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

feat: add useAsync and improve ssrRef #28

Merged
merged 25 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6473305
feat(namespace): add namespaces, rewrite ssrRef
mathe42 May 5, 2020
afbce23
feat(namespaces): add async, some refactorings
mathe42 May 5, 2020
4229694
feat(namespacer): bugfixes, now it works
mathe42 May 6, 2020
b9a40ad
Merge branch 'master' into betterAbstractAPI
mathe42 May 6, 2020
a114bf1
feat(namespace): rewrite useAsync, optimize namespaces
mathe42 May 6, 2020
a0008bd
feat(ssr-ref): optimize ssrRef, remove namespaces
mathe42 May 7, 2020
245a030
feat(useasync): remove comment
mathe42 May 7, 2020
b653f33
Apply suggestions from code review
mathe42 May 7, 2020
9cf3ded
Merge branch 'master' into betterAbstractAPI
mathe42 May 7, 2020
ca4e5e9
add tests, some refactorings
mathe42 May 7, 2020
9d90cd8
add babel for useAsync
mathe42 May 7, 2020
2dfa183
Update src/ssr-ref.ts
mathe42 May 7, 2020
f33a38e
Update src/babel.ts
mathe42 May 7, 2020
a313456
fix: typo
danielroe May 7, 2020
d064d6a
Update src/async.ts
mathe42 May 7, 2020
47f8d7d
Merge branch 'master' into betterAbstractAPI
danielroe May 8, 2020
65e9b18
fix(ssrref): fix babel plugin, add support for outside of component, …
mathe42 May 8, 2020
3419654
Update src/babel.ts
mathe42 May 8, 2020
ff11397
Merge branch 'master' into betterAbstractAPI
mathe42 May 8, 2020
3f551ea
Merge branch 'master' into betterAbstractAPI
danielroe May 8, 2020
d1ff5eb
fix test by rewrite onServerPrefetch
mathe42 May 8, 2020
fc5515c
refactor: reduce conditionality
danielroe May 8, 2020
aaf642c
fix: typo
danielroe May 8, 2020
93740a3
fix: stringify results of factory functions
danielroe May 8, 2020
96a7e1e
fix: update src/ssr-ref.ts
danielroe May 8, 2020
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
35 changes: 35 additions & 0 deletions src/async.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ssrRef } from './ssr-ref'
import { onServerPrefetch } from './server-prefetch'
import { Ref, isRef } from '@vue/composition-api'

export const useAsync = <T>(
cb: () => T | Promise<T>,
key?: string | Ref<null>
): Ref<null | T> => {
if (!key) {
throw new Error(
"You must provide a key. You can have it generated automatically by adding 'nuxt-composition-api/babel' to your Babel plugins."
)
}

const _ref = isRef(key) ? key : ssrRef<T | null>(null, key)

if (!_ref.value) {
const p = cb()

if (p instanceof Promise) {
if (process.server) {
onServerPrefetch(async () => {
_ref.value = await p
})
} else {
// eslint-disable-next-line
p.then(res => (_ref.value = res))
}
} else {
_ref.value = p
}
}

return _ref as Ref<null | T>
}
5 changes: 4 additions & 1 deletion src/babel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export default function ssrRefPlugin({ loadOptions, getEnv, types: t }: Babel) {
}
: {}),
CallExpression(path) {
if (!('name' in path.node.callee) || path.node.callee.name !== 'ssrRef')
if (
!('name' in path.node.callee) ||
!['ssrRef', 'useAsync'].includes(path.node.callee.name)
)
return

if (path.node.arguments.length > 1) return
Expand Down
3 changes: 1 addition & 2 deletions src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Vue from 'vue'
import { getCurrentInstance, onBeforeMount } from '@vue/composition-api'

import { onServerPrefetch } from './ssr-ref'
import { onServerPrefetch } from './server-prefetch'

import { ComponentInstance } from '@vue/composition-api/dist/component'

Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const meta = require('../package.json')

export { useFetch } from './fetch'
export { withContext, useContext } from './context'
export { ssrRef, onServerPrefetch, setSSRContext } from './ssr-ref'
export { ssrRef, setSSRContext } from './ssr-ref'
export { onServerPrefetch } from './server-prefetch'
export { useAsync } from './async'
export { useHead } from './meta'

export {
Expand Down
59 changes: 59 additions & 0 deletions src/server-prefetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
onServerPrefetch as onPrefetch,
getCurrentInstance,
} from '@vue/composition-api'

const ssrRefFunctions = new Map<
ReturnType<typeof getCurrentInstance>,
(() => void)[]
>()

const isPending = new Map<ReturnType<typeof getCurrentInstance>, number>()

let noSetup: Array<() => any> = []

export function onServerPrefetch(cb: () => any) {
const vm = getCurrentInstance()

if (!vm) {
noSetup.push(cb)
return
}

const pending = isPending.get(vm) || 0

isPending.set(vm, pending + 1)

onPrefetch(async () => {
await cb()

const pending = isPending.get(vm) || 0

if (pending <= 1) {
const fn = ssrRefFunctions.get(vm) || []
await Promise.all(fn.map(p => p()))
await Promise.all(noSetup.map(p => p()))
noSetup = []
} else {
isPending.set(vm, pending - 1)
}
})
}

export function onServerPrefetchEnd(cb: () => any) {
if (!process.server) return

const vm = getCurrentInstance()

if (!vm) {
noSetup.push(cb)
} else {
const fn = ssrRefFunctions.get(vm)

if (!fn) {
ssrRefFunctions.set(vm, [cb])
} else {
fn.push(cb)
}
}
}
50 changes: 23 additions & 27 deletions src/ssr-ref.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,49 @@
import { ref, Ref, onServerPrefetch as prefetch } from '@vue/composition-api'
import { ref, Ref } from '@vue/composition-api'
import { onServerPrefetchEnd } from './server-prefetch'

function getValue<T>(value: T | (() => T)): T {
if (value instanceof Function) return value()
return value
}

let data: any = {}
let injected = false

const refs: Record<string, Ref<any>> = {}

export function setSSRContext(ssrContext: any) {
ssrContext.nuxt.ssrRefs = data
}

export function injectRefs() {
if (!process.server) return

Object.entries(refs).forEach(([key, ref]) => {
data[key] = JSON.parse(JSON.stringify(ref.value))
})
function clone<T>(obj: T): T {
if (typeof obj === 'object') {
return JSON.parse(JSON.stringify(obj))
} else {
return obj
}
}

/**
* Creates a Ref that is in sync with the client.
*/
export const ssrRef = <T>(value: T | (() => T), key?: string): Ref<T> => {
const val = ref<T>(getValue(value))

if (!key)
if (!key) {
throw new Error(
"You must provide a key. You can have it generated automatically by adding 'nuxt-composition-api/babel' to your Babel plugins."
)

if (!injected) {
prefetch(injectRefs)
injected = true
}

if (process.client) {
const nuxtState = (window as any).__NUXT__
val.value = (nuxtState.ssrRefs || {})[key!] ?? getValue(value)
} else {
refs[key] = val
return ref((window as any).__NUXT__?.ssrRefs?.[key] ?? getValue(value))
}

return val as Ref<T>
}
const val = getValue(value)
const initVal = clone(val)
const _ref = ref(val) as Ref<T>

export const onServerPrefetch = (callback: Function) => {
prefetch(async () => {
await callback()
injectRefs()
onServerPrefetchEnd(() => {
if (initVal !== _ref.value) data[key] = _ref.value
})

return _ref

return _ref
}
}
2 changes: 2 additions & 0 deletions test/e2e/ssr-refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test('Shows data on ssr-loaded page', async t => {
await expectOnPage('ref-only SSR rendered')
await expectOnPage('function-runs SSR or client-side')
await expectOnPage('prefetched-result')
await expectOnPage('on: server')

await t.click(Selector('a').withText('home'))
await t.click(Selector('a').withText('ssr refs'))
Expand All @@ -27,4 +28,5 @@ test.skip('Shows appropriate data on client-loaded page', async t => {
await expectPathnameToBe('/ssr-ref')
await expectNotOnPage('ref-only SSR rendered')
await expectOnPage('function-runs SSR or client-side')
await expectOnPage('on: client')
})
21 changes: 18 additions & 3 deletions test/fixture/pages/ssr-ref.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<div>ref-{{ computedVal }}</div>
<div>function-{{ funcValue }}</div>
<div>prefetched-{{ prefetchValue }}</div>
<div>on: {{ asyncValue }}</div>
<div>no-change: {{ noChange }}</div>
<nuxt-link to="/">home</nuxt-link>
</div>
</template>
Expand All @@ -15,6 +17,7 @@ import {
useFetch,
ssrRef,
onServerPrefetch,
useAsync,
} from 'nuxt-composition-api'

export function fetcher(result, time = 100) {
Expand All @@ -27,9 +30,10 @@ export function fetcher(result, time = 100) {

export default defineComponent({
setup() {
const refValue = ssrRef('')
const prefetchValue = ssrRef('')
const funcValue = ssrRef(() => 'runs SSR or client-side')
const refValue = ssrRef('') // changed => in __NUXT__
const prefetchValue = ssrRef('') // changed => in __NUXT__
const funcValue = ssrRef(() => 'runs SSR or client-side') // function => in __NUXT__
const noChange = ssrRef('initValue') // no Change => not in __NUXT__

const computedVal = computed(() => refValue.value)

Expand All @@ -39,10 +43,21 @@ export default defineComponent({
prefetchValue.value = await fetcher('result', 500)
})

const asyncValue = useAsync(() =>
fetcher(process.server ? 'server' : 'client', 500)
)

// Error handeling
// useAsync(() => {
// throw '42'
// })

return {
computedVal,
funcValue,
prefetchValue,
asyncValue,
noChange,
}
},
})
Expand Down
9 changes: 9 additions & 0 deletions test/tsd/async.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { expectType } from 'tsd'

import { useAsync, Ref, ssrRef } from '../..'

expectType<Ref<null | { a: string }>>(useAsync(async () => ({ a: 'test' })))

expectType<Ref<null | { a: string }>>(
useAsync(async () => ({ a: 'test' }), ssrRef(null))
)
2 changes: 1 addition & 1 deletion test/tsd/ssr-ref.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { expectType } from 'tsd'

import { ssrRef, Ref } from '../..'

// eslint-disable-next-line
expectType<Ref<number>>(ssrRef(() => 42))
expectType<Ref<string>>(ssrRef('thoughtless'))
interface Obj {
name: string
}
expectType<Ref<Obj>>(ssrRef({ name: 'today' }))
expectType<Ref<null>>(ssrRef(null))
4 changes: 3 additions & 1 deletion test/unit/__snapshots__/babel-ssr-ref.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ exports[`babel plugin works 1`] = `
"const ref = ref(1);
const ref2 = ssrRef(2, \\"ref2-BAW7YFDj4E+Qxr+ujqEADg==\\");
const ref3 = ssrRef(3, 'custom-key');
const ref4 = ssrRef(4, \\"ref4-h6IKM1doqCRBR49lWv2V/g==\\");"
const ref4 = ssrRef(() => 4, \\"ref4-h6IKM1doqCRBR49lWv2V/g==\\");
const async1 = useAsync(() => null, \\"async1-nk1uJ/q39HMoKEMFH33Ryg==\\");
const async2 = useAsync(() => null, 'key');"
`;
5 changes: 4 additions & 1 deletion test/unit/babel-ssr-ref.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ var example = `
const ref = ref(1)
const ref2 = ssrRef(2)
const ref3 = ssrRef(3, 'custom-key')
const ref4 = ssrRef(4)
const ref4 = ssrRef(() => 4)

const async1 = useAsync(() => null)
const async2 = useAsync(() => null, 'key')
`

describe('babel plugin', () => {
Expand Down