Instantly share code, notes, and snippets.
-
Star
(1)
1
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save tommie/23539b45f55547d54eac3fdcf8d14231 to your computer and use it in GitHub Desktop.
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
// Based off of https://github.com/nuxt/nuxt/blob/95fab0a2dd59b7cce9f73c8d66b7f518b786aea6/packages/nuxt/src/app/composables/cookie.ts. | |
// | |
// This allows generating the CookieSerializeOptions at the time of | |
// setting the cookie, so that expiration doesn't have to be | |
// determined at definition. | |
import type { Ref, UnwrapRef } from 'vue' | |
import { getCurrentInstance, nextTick, onUnmounted, ref, watch } from 'vue' | |
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es' | |
import { parse, serialize } from 'cookie-es' | |
import { deleteCookie, getCookie, setCookie } from 'h3' | |
import type { H3Event } from 'h3' | |
import destr from 'destr' | |
import { isEqual } from 'ohash' | |
import { useNuxtApp } from '#app' | |
import { useRequestEvent } from '#app/composables/ssr' | |
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'> | |
export interface CookieOptions<T = any> extends _CookieOptions { | |
decode(value: string): T | |
encode(value: T): string | |
default?: () => T | Ref<T> | |
watch: boolean | 'shallow' | |
transformSerializeOptions: (opts: CookieOptions<T>, value: UnwrapRef<T> | undefined) => CookieSerializeOptions | |
} | |
export type CookieRef<T> = Ref<UnwrapRef<T> | undefined> | |
const CookieDefaults: CookieOptions<any> = { | |
path: '/', | |
watch: true, | |
decode: val => destr(decodeURIComponent(val)), | |
encode: val => encodeURIComponent(typeof val === 'string' ? val : JSON.stringify(val)), | |
transformSerializeOptions: opts => opts, | |
} | |
export function useConfigurableCookie<T> (name: string, _opts: Partial<CookieOptions<T>> = {}): CookieRef<T> { | |
const opts = { ...CookieDefaults, ..._opts } | |
const cookies = readRawCookies(opts) || {} | |
const cookie = ref<T | undefined>(cookies[name] ?? opts.default?.()) | |
if (process.client) { | |
const channel = typeof BroadcastChannel === 'undefined' ? null : new BroadcastChannel(`nuxt:cookies:${name}`) | |
if (getCurrentInstance()) { onUnmounted(() => { channel?.close() }) } | |
const callback = () => { | |
writeClientCookie(name, cookie.value, opts.transformSerializeOptions(opts, cookie.value)) | |
channel?.postMessage(cookie.value) | |
} | |
let watchPaused = false | |
if (channel) { | |
channel.onmessage = (event) => { | |
watchPaused = true | |
cookie.value = event.data | |
nextTick(() => { watchPaused = false }) | |
} | |
} | |
if (opts.watch) { | |
watch(cookie, (newVal, oldVal) => { | |
if (watchPaused || isEqual(newVal, oldVal)) { return } | |
callback() | |
}, | |
{ deep: opts.watch !== 'shallow' }) | |
} else { | |
callback() | |
} | |
} else if (process.server) { | |
const nuxtApp = useNuxtApp() | |
const writeFinalCookieValue = () => { | |
if (!isEqual(cookie.value, cookies[name])) { | |
writeServerCookie(useRequestEvent(nuxtApp), name, cookie.value, opts.transformSerializeOptions(opts, cookie.value)) | |
} | |
} | |
const unhook = nuxtApp.hooks.hookOnce('app:rendered', writeFinalCookieValue) | |
nuxtApp.hooks.hookOnce('app:error', () => { | |
unhook() // don't write cookie subsequently when app:rendered is called | |
return writeFinalCookieValue() | |
}) | |
} | |
return cookie | |
} | |
function readRawCookies (opts: CookieOptions): Record<string, any> | undefined { | |
if (process.server) { | |
return parse(useRequestEvent()?.node.req.headers.cookie || '', opts) | |
} else if (process.client) { | |
return parse(document.cookie, opts) | |
} | |
} | |
function serializeCookie (name: string, value: any, opts: CookieSerializeOptions) { | |
if (value === null || value === undefined) { | |
return serialize(name, value, { ...opts, maxAge: -1 }) | |
} | |
return serialize(name, value, opts) | |
} | |
function writeClientCookie (name: string, value: any, opts: CookieSerializeOptions) { | |
document.cookie = serializeCookie(name, value, opts) | |
} | |
function writeServerCookie (event: H3Event, name: string, value: any, opts: CookieSerializeOptions) { | |
if (event) { | |
// update if value is set | |
if (value !== null && value !== undefined) { | |
return setCookie(event, name, value, opts) | |
} | |
// delete if cookie exists in browser and value is null/undefined | |
if (getCookie(event, name) !== undefined) { | |
return deleteCookie(event, name, opts) | |
} | |
// else ignore if cookie doesn't exist in browser and value is null/undefined | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment