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 14 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"release": "release-it",
"test": "run-s test:*",
"test:types": "tsd",
"test:e2e": "testcafe firefox:headless test/e2e --app 'node test/start-fixture.js'"
"test:e2e": "testcafe firefox:headless test/e2e --app node test/start-fixture.js"
danielroe marked this conversation as resolved.
Show resolved Hide resolved
},
"tsd": {
"directory": "test/tsd",
Expand Down
29 changes: 29 additions & 0 deletions src/async.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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> => {
const _ref = isRef(key) ? key : ssrRef<T | null>(null, key)
mathe42 marked this conversation as resolved.
Show resolved Hide resolved

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))
mathe42 marked this conversation as resolved.
Show resolved Hide resolved
}
} 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 } 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
24 changes: 24 additions & 0 deletions src/server-prefetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { onServerPrefetch as onPrefetch } from '@vue/composition-api'
const prefetchFunctions: Array<() => any> = []
const prefetchFunctionsEnd: Array<() => any> = []

let hasServerPrefetch = false
function setupOnServerPrefetch() {
if (!hasServerPrefetch) {
hasServerPrefetch = true
onPrefetch(async () => {
await Promise.all(prefetchFunctions.map(p => p()))
await Promise.all(prefetchFunctionsEnd.map(p => p()))
})
}
}

export function onServerPrefetch(cb: () => any) {
prefetchFunctions.push(cb)
setupOnServerPrefetch()
}

export function onServerPrefetchEnd(cb: () => any) {
danielroe marked this conversation as resolved.
Show resolved Hide resolved
prefetchFunctionsEnd.push(cb)
setupOnServerPrefetch()
}
64 changes: 31 additions & 33 deletions src/ssr-ref.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,53 @@
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 ssrContext: any
let injected = false
let data: any = {}

const refs: [string, Ref<any>][] = []

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

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

if (!ssrContext.nuxt.ssrRefs) ssrContext.nuxt.ssrRefs = {}

refs.forEach(([key, ref]) => {
ssrContext.nuxt.ssrRefs[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.push([key, val])
return ref((window as any).__NUXT__?.ssrRefs?.[key] ?? getValue(value))
}

return val as Ref<T>
}
if (typeof value === 'function') {
const sref = ref(getValue(value)) as Ref<T>
onServerPrefetchEnd(() => (data[key] = sref.value))

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

export const onServerPrefetch = (callback: Function) => {
prefetch(async () => {
await callback()
injectRefs()
})
onServerPrefetchEnd(() =>
initVal !== sref.value ? (data[key] = sref.value) : null
danielroe marked this conversation as resolved.
Show resolved Hide resolved
)
mathe42 marked this conversation as resolved.
Show resolved Hide resolved

return sref
}
}
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 @@ -26,4 +27,5 @@ test('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(() =>
mathe42 marked this conversation as resolved.
Show resolved Hide resolved
fetcher(process.server ? 'server' : 'client', 500)
)

// Error handeling
// useAsync(() => {
danielroe marked this conversation as resolved.
Show resolved Hide resolved
// 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