-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
ssrRef
capability for automatic SSR support (#23)
- Loading branch information
Showing
11 changed files
with
277 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import * as types from '@babel/types' | ||
import { Visitor } from '@babel/traverse' | ||
import crypto from 'crypto' | ||
|
||
interface Babel { | ||
types: typeof types | ||
loadOptions: () => Record<string, any> | ||
getEnv: () => string | ||
} | ||
|
||
export default function ssrRefPlugin({ loadOptions, getEnv, types: t }: Babel) { | ||
const env = getEnv() | ||
const cwd = env === 'test' ? '' : loadOptions().cwd | ||
|
||
let varName = '' | ||
const visitor: Visitor = { | ||
...(env !== 'production' | ||
? { | ||
VariableDeclarator(path) { | ||
varName = 'name' in path.node.id ? `${path.node.id.name}-` : '' | ||
}, | ||
} | ||
: {}), | ||
CallExpression(path) { | ||
if (!('name' in path.node.callee) || path.node.callee.name !== 'ssrRef') | ||
return | ||
|
||
if (path.node.arguments.length > 1) return | ||
const hash = crypto.createHash('md5') | ||
|
||
hash.update(`${cwd}-${path.node.callee.start}`) | ||
const digest = hash.digest('base64').toString() | ||
path.node.arguments.push(t.stringLiteral(`${varName}${digest}`)) | ||
}, | ||
} | ||
return { visitor } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { | ||
ref, | ||
onServerPrefetch as prefetch, | ||
getCurrentInstance, | ||
Ref, | ||
} from '@vue/composition-api' | ||
|
||
function getValue(value: any) { | ||
if (typeof value === 'function') return value() | ||
return value | ||
} | ||
|
||
let ssrContext: any | ||
|
||
const refs: [string, Ref<any>][] = [] | ||
|
||
export function injectRefs() { | ||
if (!process.server) return | ||
|
||
if (!ssrContext.nuxt.ssrRefs) ssrContext.nuxt.ssrRefs = {} | ||
|
||
refs.forEach(([key, ref]) => { | ||
ssrContext.nuxt.ssrRefs[key] = ref.value | ||
}) | ||
} | ||
|
||
export const ssrRef = <T>(value: T, key?: string) => { | ||
const val = ref<T>(getValue(value)) | ||
const vm = getCurrentInstance()! | ||
|
||
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 (!ssrContext) { | ||
ssrContext = ssrContext || vm.$ssrContext | ||
prefetch(injectRefs) | ||
} | ||
|
||
if (process.client) { | ||
const nuxtState = (window as any).__NUXT__ | ||
val.value = (nuxtState.ssrRefs || {})[key!] ?? getValue(value) | ||
} else { | ||
refs.push([key, val]) | ||
} | ||
|
||
return val | ||
} | ||
|
||
export const onServerPrefetch = (callback: Function) => { | ||
prefetch(async () => { | ||
await callback() | ||
injectRefs() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Selector } from 'testcafe' | ||
import { | ||
navigateTo, | ||
expectOnPage, | ||
expectPathnameToBe, | ||
expectNotOnPage, | ||
} from './helpers' | ||
|
||
// eslint-disable-next-line | ||
fixture`SSR Refs` | ||
|
||
test('Shows data on ssr-loaded page', async t => { | ||
await navigateTo('/ssr-ref') | ||
await expectOnPage('ref-only SSR rendered') | ||
await expectOnPage('function-runs SSR or client-side') | ||
await expectOnPage('prefetched-result') | ||
|
||
await t.click(Selector('a').withText('home')) | ||
await t.click(Selector('a').withText('ssr refs')) | ||
await expectOnPage('ref-only SSR rendered') | ||
}) | ||
|
||
test('Shows appropriate data on client-loaded page', async t => { | ||
await navigateTo('/') | ||
await t.click(Selector('a').withText('ssr refs')) | ||
await expectPathnameToBe('/ssr-ref') | ||
await expectNotOnPage('ref-only SSR rendered') | ||
await expectOnPage('function-runs SSR or client-side') | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<template> | ||
<div> | ||
<div>ref-{{ computedVal }}</div> | ||
<div>function-{{ funcValue }}</div> | ||
<div>prefetched-{{ prefetchValue }}</div> | ||
<nuxt-link to="/">home</nuxt-link> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import { defineComponent, ref, computed, useFetch, ssrRef, onServerPrefetch } from 'nuxt-composition-api' | ||
export function fetcher(result, time = 100) { | ||
return new Promise(resolve => { | ||
return setTimeout(() => { | ||
resolve(result) | ||
}, time) | ||
}) | ||
} | ||
export default defineComponent({ | ||
setup() { | ||
const refValue = ssrRef('') | ||
const prefetchValue = ssrRef('') | ||
const funcValue = ssrRef(() => 'runs SSR or client-side') | ||
const computedVal = computed(() => refValue.value) | ||
if (process.server) refValue.value = 'only SSR rendered' | ||
onServerPrefetch(async () => { | ||
prefetchValue.value = await fetcher('result', 500) | ||
}) | ||
return { | ||
computedVal, | ||
funcValue, | ||
prefetchValue, | ||
} | ||
}, | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
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==\\");" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const babel = require('@babel/core') | ||
const plugin = require('../../lib/babel') | ||
/* eslint-enable */ | ||
|
||
var example = ` | ||
const ref = ref(1) | ||
const ref2 = ssrRef(2) | ||
const ref3 = ssrRef(3, 'custom-key') | ||
const ref4 = ssrRef(4) | ||
` | ||
|
||
describe('babel plugin', () => { | ||
it('works', () => { | ||
const { code } = babel.transform(example, { plugins: [plugin] })! | ||
expect(code).toMatchSnapshot() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters