Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,4 @@
export * from 'vue';
export declare const install: () => void;
export declare function set(target: any, key: string | number | symbol, val: any): any;
export declare function del(target: any, key: string | number | symbol): void;

View file

@ -0,0 +1,19 @@
export * from "vue";
export const install = () => {
};
export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val;
}
target[key] = val;
return val;
}
export function del(target, key) {
if (Array.isArray(target)) {
target.splice(key, 1);
return;
}
delete target[key];
}

View file

@ -0,0 +1,2 @@
export declare const requestIdleCallback: Window['requestIdleCallback'];
export declare const cancelIdleCallback: Window['cancelIdleCallback'];

View file

@ -0,0 +1,15 @@
export const requestIdleCallback = import.meta.server ? (() => {
}) : globalThis.requestIdleCallback || ((cb) => {
const start = Date.now();
const idleDeadline = {
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
};
return setTimeout(() => {
cb(idleDeadline);
}, 1);
});
export const cancelIdleCallback = import.meta.server ? (() => {
}) : globalThis.cancelIdleCallback || ((id) => {
clearTimeout(id);
});

View file

@ -0,0 +1 @@
export declare const setInterval: typeof globalThis.setInterval | (() => void);

View file

@ -0,0 +1,11 @@
import { createError } from "../composables/error.js";
const intervalError = "[nuxt] `setInterval` should not be used on the server. Consider wrapping it with an `onNuxtReady`, `onBeforeMount` or `onMounted` lifecycle hook, or ensure you only call it in the browser by checking `import.meta.client`.";
export const setInterval = import.meta.client ? globalThis.setInterval : () => {
if (import.meta.dev) {
throw createError({
statusCode: 500,
message: intervalError
});
}
console.error(intervalError);
};

View file

@ -0,0 +1,4 @@
export * from './capi.js';
export declare const Vue2: undefined;
export declare const isVue2 = false;
export declare const isVue3 = true;

View file

@ -0,0 +1,4 @@
export * from "./capi.js";
export const Vue2 = void 0;
export const isVue2 = false;
export const isVue3 = true;

View file

@ -0,0 +1,50 @@
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
fallbackTag: {
type: StringConstructor;
default: () => string;
};
fallback: {
type: StringConstructor;
default: () => string;
};
placeholder: {
type: StringConstructor;
};
placeholderTag: {
type: StringConstructor;
};
keepFallback: {
type: BooleanConstructor;
default: () => boolean;
};
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "ssr-error"[], "ssr-error", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
fallbackTag: {
type: StringConstructor;
default: () => string;
};
fallback: {
type: StringConstructor;
default: () => string;
};
placeholder: {
type: StringConstructor;
};
placeholderTag: {
type: StringConstructor;
};
keepFallback: {
type: BooleanConstructor;
default: () => boolean;
};
}>> & Readonly<{
"onSsr-error"?: ((...args: any[]) => any) | undefined;
}>, {
fallback: string;
fallbackTag: string;
keepFallback: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,50 @@
import { createElementBlock, defineComponent, onMounted, shallowRef, useId } from "vue";
import { useState } from "../composables/state.js";
export default defineComponent({
name: "NuxtClientFallback",
inheritAttrs: false,
props: {
fallbackTag: {
type: String,
default: () => "div"
},
fallback: {
type: String,
default: () => ""
},
placeholder: {
type: String
},
placeholderTag: {
type: String
},
keepFallback: {
type: Boolean,
default: () => false
}
},
emits: ["ssr-error"],
setup(props, ctx) {
const mounted = shallowRef(false);
const ssrFailed = useState(useId());
if (ssrFailed.value) {
onMounted(() => {
mounted.value = true;
});
}
return () => {
if (ssrFailed.value) {
if (!mounted.value || props.keepFallback) {
const slot = ctx.slots.placeholder || ctx.slots.fallback;
if (slot) {
return slot();
}
const fallbackStr = props.placeholder || props.fallback;
const fallbackTag = props.placeholderTag || props.fallbackTag;
return createElementBlock(fallbackTag, null, fallbackStr);
}
}
return ctx.slots.default?.();
};
}
});

View file

@ -0,0 +1,57 @@
declare const NuxtClientFallbackServer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
fallbackTag: {
type: StringConstructor;
default: () => string;
};
fallback: {
type: StringConstructor;
default: () => string;
};
placeholder: {
type: StringConstructor;
};
placeholderTag: {
type: StringConstructor;
};
keepFallback: {
type: BooleanConstructor;
default: () => boolean;
};
}>, {
ssrFailed: import("vue").ShallowRef<boolean, boolean>;
ssrVNodes: {
getBuffer(): import("./utils.js").SSRBuffer;
push(item: import("./utils.js").SSRBufferItem): void;
};
} | {
ssrFailed: boolean;
ssrVNodes: never[];
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
'ssr-error'(_error: unknown): true;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
fallbackTag: {
type: StringConstructor;
default: () => string;
};
fallback: {
type: StringConstructor;
default: () => string;
};
placeholder: {
type: StringConstructor;
};
placeholderTag: {
type: StringConstructor;
};
keepFallback: {
type: BooleanConstructor;
default: () => boolean;
};
}>> & Readonly<{
"onSsr-error"?: ((_error: unknown) => any) | undefined;
}>, {
fallback: string;
fallbackTag: string;
keepFallback: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default NuxtClientFallbackServer;

View file

@ -0,0 +1,80 @@
import { defineComponent, getCurrentInstance, onErrorCaptured, shallowRef, useId } from "vue";
import { ssrRenderAttrs, ssrRenderSlot, ssrRenderVNode } from "vue/server-renderer";
import { isPromise } from "@vue/shared";
import { useState } from "../composables/state.js";
import { createBuffer } from "./utils.js";
const NuxtClientFallbackServer = defineComponent({
name: "NuxtClientFallback",
inheritAttrs: false,
props: {
fallbackTag: {
type: String,
default: () => "div"
},
fallback: {
type: String,
default: () => ""
},
placeholder: {
type: String
},
placeholderTag: {
type: String
},
keepFallback: {
type: Boolean,
default: () => false
}
},
emits: {
"ssr-error"(_error) {
return true;
}
},
async setup(_, ctx) {
const vm = getCurrentInstance();
const ssrFailed = shallowRef(false);
const error = useState(useId());
onErrorCaptured((err) => {
error.value = true;
ssrFailed.value = true;
ctx.emit("ssr-error", err);
return false;
});
try {
const defaultSlot = ctx.slots.default?.();
const ssrVNodes = createBuffer();
if (defaultSlot) {
for (let i = 0; i < defaultSlot.length; i++) {
ssrRenderVNode(ssrVNodes.push, defaultSlot[i], vm);
}
}
const buffer = ssrVNodes.getBuffer();
if (buffer.hasAsync) {
await Promise.all(buffer.filter(isPromise));
}
return { ssrFailed, ssrVNodes };
} catch (ssrError) {
error.value = true;
ctx.emit("ssr-error", ssrError);
return { ssrFailed: true, ssrVNodes: [] };
}
},
ssrRender(ctx, push, parent) {
if (ctx.ssrFailed) {
const { fallback, placeholder } = ctx.$slots;
if (fallback || placeholder) {
ssrRenderSlot(ctx.$slots, fallback ? "fallback" : "placeholder", {}, null, push, parent);
} else {
const content = ctx.placeholder || ctx.fallback;
const tag = ctx.placeholderTag || ctx.fallbackTag;
push(`<${tag}${ssrRenderAttrs(ctx.$attrs)}>${content}</${tag}>`);
}
} else {
push("<!--[-->");
push(ctx.ssrVNodes.getBuffer());
push("<!--]-->");
}
}
});
export default NuxtClientFallbackServer;

View file

@ -0,0 +1,26 @@
import type { ComponentOptions, InjectionKey, SlotsType, VNode } from 'vue';
export declare const clientOnlySymbol: InjectionKey<boolean>;
declare const _default: import("vue").DefineComponent<{
placeholder?: any;
fallback?: any;
placeholderTag?: any;
fallbackTag?: any;
}, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
placeholder?: any;
fallback?: any;
placeholderTag?: any;
fallbackTag?: any;
}> & Readonly<{}>, {}, SlotsType<{
default?: () => VNode[];
/**
* Specify a content to be rendered on the server and displayed until `<ClientOnly>` is mounted in the browser.
*/
fallback?: () => VNode[];
placeholder?: () => VNode[];
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;
export declare function createClientOnly<T extends ComponentOptions>(component: T): any;

View file

@ -0,0 +1,130 @@
import { cloneVNode, createElementBlock, defineComponent, getCurrentInstance, h, onMounted, provide, shallowRef } from "vue";
import { isPromise } from "@vue/shared";
import { useNuxtApp } from "../nuxt.js";
import ServerPlaceholder from "./server-placeholder.js";
import { elToStaticVNode } from "./utils.js";
export const clientOnlySymbol = Symbol.for("nuxt:client-only");
const STATIC_DIV = "<div></div>";
export default defineComponent({
name: "ClientOnly",
inheritAttrs: false,
props: ["fallback", "placeholder", "placeholderTag", "fallbackTag"],
...import.meta.dev && {
slots: Object
},
setup(props, { slots, attrs }) {
const mounted = shallowRef(false);
onMounted(() => {
mounted.value = true;
});
if (import.meta.dev) {
const nuxtApp = useNuxtApp();
nuxtApp._isNuxtPageUsed = true;
nuxtApp._isNuxtLayoutUsed = true;
}
const vm = getCurrentInstance();
if (vm) {
vm._nuxtClientOnly = true;
}
provide(clientOnlySymbol, true);
return () => {
if (mounted.value) {
const vnodes = slots.default?.();
if (vnodes && vnodes.length === 1) {
return [cloneVNode(vnodes[0], attrs)];
}
return vnodes;
}
const slot = slots.fallback || slots.placeholder;
if (slot) {
return h(slot);
}
const fallbackStr = props.fallback || props.placeholder || "";
const fallbackTag = props.fallbackTag || props.placeholderTag || "span";
return createElementBlock(fallbackTag, attrs, fallbackStr);
};
}
});
const cache = /* @__PURE__ */ new WeakMap();
// @__NO_SIDE_EFFECTS__
export function createClientOnly(component) {
if (import.meta.server) {
return ServerPlaceholder;
}
if (cache.has(component)) {
return cache.get(component);
}
const clone = { ...component };
if (clone.render) {
clone.render = (ctx, cache2, $props, $setup, $data, $options) => {
if ($setup.mounted$ ?? ctx.mounted$) {
const res = component.render?.bind(ctx)(ctx, cache2, $props, $setup, $data, $options);
return res.children === null || typeof res.children === "string" ? cloneVNode(res) : h(res);
}
return elToStaticVNode(ctx._.vnode.el, STATIC_DIV);
};
} else {
clone.template &&= `
<template v-if="mounted$">${component.template}</template>
<template v-else>${STATIC_DIV}</template>
`;
}
clone.setup = (props, ctx) => {
const nuxtApp = useNuxtApp();
const mounted$ = shallowRef(nuxtApp.isHydrating === false);
const instance = getCurrentInstance();
if (nuxtApp.isHydrating) {
const attrs = { ...instance.attrs };
const directives = extractDirectives(instance);
for (const key in attrs) {
delete instance.attrs[key];
}
onMounted(() => {
Object.assign(instance.attrs, attrs);
instance.vnode.dirs = directives;
});
}
onMounted(() => {
mounted$.value = true;
});
const setupState = component.setup?.(props, ctx) || {};
if (isPromise(setupState)) {
return Promise.resolve(setupState).then((setupState2) => {
if (typeof setupState2 !== "function") {
setupState2 ||= {};
setupState2.mounted$ = mounted$;
return setupState2;
}
return (...args) => {
if (mounted$.value || !nuxtApp.isHydrating) {
const res = setupState2(...args);
return res.children === null || typeof res.children === "string" ? cloneVNode(res) : h(res);
}
return elToStaticVNode(instance?.vnode.el, STATIC_DIV);
};
});
} else {
if (typeof setupState === "function") {
return (...args) => {
if (mounted$.value) {
const res = setupState(...args);
const attrs = clone.inheritAttrs !== false ? ctx.attrs : void 0;
return res.children === null || typeof res.children === "string" ? cloneVNode(res, attrs) : h(res, attrs);
}
return elToStaticVNode(instance?.vnode.el, STATIC_DIV);
};
}
return Object.assign(setupState, { mounted$ });
}
};
cache.set(component, clone);
return clone;
}
function extractDirectives(instance) {
if (!instance || !instance.vnode.dirs) {
return null;
}
const directives = instance.vnode.dirs;
instance.vnode.dirs = null;
return directives;
}

View file

@ -0,0 +1,11 @@
import type { SlotsType, VNode } from 'vue';
declare const _default: import("vue").DefineComponent<{}, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, SlotsType<{
default?: () => VNode[];
/**
* If you ever require to have a replacement during production.
*/
fallback?: () => VNode[];
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,14 @@
import { defineComponent } from "vue";
export default defineComponent({
name: "DevOnly",
inheritAttrs: false,
...import.meta.dev && {
slots: Object
},
setup(_, props) {
if (import.meta.dev) {
return () => props.slots.default?.();
}
return () => props.slots.fallback?.();
}
});

View file

@ -0,0 +1,52 @@
<script setup>
import { useHead } from "#imports";
const props = defineProps({
appName: {
type: String,
default: "Nuxt"
},
version: {
type: String,
default: ""
},
statusCode: {
type: Number,
default: 404
},
statusMessage: {
type: String,
default: "Not Found"
},
description: {
type: String,
default: "Sorry, the page you are looking for could not be found."
},
backHome: {
type: String,
default: "Go back home"
}
});
useHead({
title: `${props.statusCode} - ${props.statusMessage} | ${props.appName}`,
script: [
{
innerHTML: `!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)})).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`
}
],
style: [
{
innerHTML: `*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}h1,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }`
}
]
});
</script>
<template>
<div class="antialiased bg-white dark:bg-black dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-black"><div class="fixed left-0 right-0 spotlight z-10"></div><div class="max-w-520px text-center z-20"><h1 class="font-medium mb-8 sm:text-10xl text-8xl" v-text="statusCode" /><p class="font-light leading-tight mb-16 px-8 sm:px-0 sm:text-4xl text-xl" v-text="description" /><div class="flex items-center justify-center w-full"><NuxtLink to="/" class="cursor-pointer gradient-border px-4 py-2 sm:px-6 sm:py-3 sm:text-xl text-md">
{{ backHome }}
</NuxtLink></div></div></div>
</template>
<style scoped>
.spotlight{background:linear-gradient(45deg,#00dc82,#36e4da 50%,#0047e1);bottom:-30vh;filter:blur(20vh);height:40vh}.gradient-border{backdrop-filter:blur(10px);border-radius:.5rem;position:relative}@media (prefers-color-scheme:light){.gradient-border{background-color:hsla(0,0%,100%,.3)}.gradient-border:before{background:linear-gradient(90deg,#e2e2e2,#e2e2e2 25%,#00dc82 50%,#36e4da 75%,#0047e1)}}@media (prefers-color-scheme:dark){.gradient-border{background-color:hsla(0,0%,8%,.3)}.gradient-border:before{background:linear-gradient(90deg,#303030,#303030 25%,#00dc82 50%,#36e4da 75%,#0047e1)}}.gradient-border:before{background-size:400% auto;border-radius:.5rem;bottom:0;content:"";left:0;-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.5;padding:2px;position:absolute;right:0;top:0;transition:background-position .3s ease-in-out,opacity .2s ease-in-out;width:100%}.gradient-border:hover:before{background-position:-50% 0;opacity:1}.fixed{position:fixed}.left-0{left:0}.right-0{right:0}.z-10{z-index:10}.z-20{z-index:20}.grid{display:grid}.mb-16{margin-bottom:4rem}.mb-8{margin-bottom:2rem}.max-w-520px{max-width:520px}.min-h-screen{min-height:100vh}.w-full{width:100%}.flex{display:flex}.cursor-pointer{cursor:pointer}.place-content-center{place-content:center}.items-center{align-items:center}.justify-center{justify-content:center}.overflow-hidden{overflow:hidden}.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.text-center{text-align:center}.text-8xl{font-size:6rem;line-height:1}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-black{--un-text-opacity:1;color:rgb(0 0 0/var(--un-text-opacity))}.font-light{font-weight:300}.font-medium{font-weight:500}.leading-tight{line-height:1.25}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\:bg-black{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}.dark\:text-white{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-3{padding-bottom:.75rem;padding-top:.75rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}}
</style>

View file

@ -0,0 +1,60 @@
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
statusCode: {
type: NumberConstructor;
default: number;
};
statusMessage: {
type: StringConstructor;
default: string;
};
description: {
type: StringConstructor;
default: string;
};
backHome: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
statusCode: {
type: NumberConstructor;
default: number;
};
statusMessage: {
type: StringConstructor;
default: string;
};
description: {
type: StringConstructor;
default: string;
};
backHome: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
appName: string;
version: string;
statusCode: number;
statusMessage: string;
description: string;
backHome: string;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,46 @@
<script setup>
import { useHead } from "#imports";
const props = defineProps({
appName: {
type: String,
default: "Nuxt"
},
version: {
type: String,
default: ""
},
statusCode: {
type: Number,
default: 500
},
statusMessage: {
type: String,
default: "Server error"
},
description: {
type: String,
default: "This page is temporarily unavailable."
}
});
useHead({
title: `${props.statusCode} - ${props.statusMessage} | ${props.appName}`,
script: [
{
innerHTML: `!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)})).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`
}
],
style: [
{
innerHTML: `*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1{font-size:inherit;font-weight:inherit}h1,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }`
}
]
});
</script>
<template>
<div class="antialiased bg-white dark:bg-black dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-black"><div class="-bottom-1/2 fixed h-1/2 left-0 right-0 spotlight"></div><div class="max-w-520px text-center"><h1 class="font-medium mb-8 sm:text-10xl text-8xl" v-text="statusCode" /><p class="font-light leading-tight mb-16 px-8 sm:px-0 sm:text-4xl text-xl" v-text="description" /></div></div>
</template>
<style scoped>
.spotlight{background:linear-gradient(45deg,#00dc82,#36e4da 50%,#0047e1);filter:blur(20vh)}.fixed{position:fixed}.-bottom-1\/2{bottom:-50%}.left-0{left:0}.right-0{right:0}.grid{display:grid}.mb-16{margin-bottom:4rem}.mb-8{margin-bottom:2rem}.h-1\/2{height:50%}.max-w-520px{max-width:520px}.min-h-screen{min-height:100vh}.place-content-center{place-content:center}.overflow-hidden{overflow:hidden}.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-8{padding-left:2rem;padding-right:2rem}.text-center{text-align:center}.text-8xl{font-size:6rem;line-height:1}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-black{--un-text-opacity:1;color:rgb(0 0 0/var(--un-text-opacity))}.font-light{font-weight:300}.font-medium{font-weight:500}.leading-tight{line-height:1.25}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\:bg-black{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}.dark\:text-white{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\:px-0{padding-left:0;padding-right:0}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}}
</style>

View file

@ -0,0 +1,51 @@
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
statusCode: {
type: NumberConstructor;
default: number;
};
statusMessage: {
type: StringConstructor;
default: string;
};
description: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
statusCode: {
type: NumberConstructor;
default: number;
};
statusMessage: {
type: StringConstructor;
default: string;
};
description: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
appName: string;
version: string;
statusCode: number;
statusMessage: string;
description: string;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,3 @@
export { defineNuxtLink } from './nuxt-link.js';
export type { NuxtLinkOptions, NuxtLinkProps } from './nuxt-link.js';
export type { NuxtTimeProps } from './nuxt-time.vue.js';

View file

@ -0,0 +1 @@
export { defineNuxtLink } from "./nuxt-link.js";

View file

@ -0,0 +1,7 @@
import type { InjectionKey } from 'vue';
import type { RouteLocationNormalizedLoaded } from 'vue-router';
export interface LayoutMeta {
isCurrent: (route: RouteLocationNormalizedLoaded) => boolean;
}
export declare const LayoutMetaSymbol: InjectionKey<LayoutMeta>;
export declare const PageRouteSymbol: InjectionKey<RouteLocationNormalizedLoaded>;

View file

@ -0,0 +1,2 @@
export const LayoutMetaSymbol = Symbol("layout-meta");
export const PageRouteSymbol = Symbol("route");

View file

@ -0,0 +1,20 @@
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
context: {
type: () => {
name: string;
props?: Record<string, any>;
};
required: true;
};
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
context: {
type: () => {
name: string;
props?: Record<string, any>;
};
required: true;
};
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,28 @@
import { createVNode, defineComponent, onErrorCaptured } from "vue";
import { injectHead } from "../composables/head.js";
import { createError } from "../composables/error.js";
import { islandComponents } from "#build/components.islands.mjs";
export default defineComponent({
name: "IslandRenderer",
props: {
context: {
type: Object,
required: true
}
},
setup(props) {
const head = injectHead();
head.entries.clear();
const component = islandComponents[props.context.name];
if (!component) {
throw createError({
statusCode: 404,
statusMessage: `Island component not found: ${props.context.name}`
});
}
onErrorCaptured((e) => {
console.log(e);
});
return () => createVNode(component || "span", { ...props.context.props, "data-island-uid": "" });
}
});

View file

@ -0,0 +1 @@
export { default } from './nuxt-layout.js';

View file

@ -0,0 +1 @@
export { default } from "./nuxt-layout.js";

View file

@ -0,0 +1,46 @@
<template>
<slot
v-if="error"
v-bind="{ error, clearError }"
name="error"
/>
<slot
v-else
name="default"
/>
</template>
<script setup>
import { onErrorCaptured, shallowRef } from "vue";
import { useNuxtApp } from "../nuxt";
import { onNuxtReady } from "../composables/ready";
defineOptions({
name: "NuxtErrorBoundary",
inheritAttrs: false
});
const emit = defineEmits(["error"]);
defineSlots();
const error = shallowRef(null);
function clearError() {
error.value = null;
}
if (import.meta.client) {
let handleError = function(...args) {
const [err, instance, info] = args;
emit("error", err);
nuxtApp.hooks.callHook("vue:error", err, instance, info);
error.value = err;
};
const nuxtApp = useNuxtApp();
onErrorCaptured((err, instance, info) => {
if (!nuxtApp.isHydrating) {
handleError(err, instance, info);
} else {
onNuxtReady(() => handleError(err, instance, info));
}
return false;
});
}
defineExpose({ error, clearError });
</script>

View file

@ -0,0 +1,24 @@
type __VLS_Slots = {
error(props: {
error: Error;
clearError: () => void;
}): any;
default(): any;
};
declare function clearError(): void;
declare const __VLS_base: import("vue").DefineComponent<{}, {
error: import("vue").ShallowRef<Error | null, Error | null>;
clearError: typeof clearError;
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
error: (error: Error) => any;
}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
onError?: ((error: Error) => any) | undefined;
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
declare const _default: typeof __VLS_export;
export default _default;
type __VLS_WithSlots<T, S> = T & {
new (): {
$slots: S;
};
};

View file

@ -0,0 +1,27 @@
<template>
<ErrorTemplate v-bind="{ statusCode, statusMessage, description, stack }" />
</template>
<script setup>
import { defineAsyncComponent } from "vue";
import { escapeHtml } from "@vue/shared";
const props = defineProps({
error: Object
});
const _error = props.error;
const stacktrace = import.meta.dev && _error.stack ? _error.stack.split("\n").splice(1).map((line) => {
const text = line.replace("webpack:/", "").replace(".vue", ".js").trim();
return {
text,
internal: line.includes("node_modules") && !line.includes(".cache") || line.includes("internal") || line.includes("new Promise")
};
}).map((i) => `<span class="stack${i.internal ? " internal" : ""}">${escapeHtml(i.text)}</span>`).join("\n") : "";
const statusCode = Number(_error.statusCode || 500);
const is404 = statusCode === 404;
const statusMessage = _error.statusMessage ?? (is404 ? "Page Not Found" : "Internal Server Error");
const description = _error.message || _error.toString();
const stack = import.meta.dev && !is404 ? _error.description || `<pre>${stacktrace}</pre>` : void 0;
const _Error404 = defineAsyncComponent(() => import("./error-404.vue"));
const _Error = defineAsyncComponent(() => import("./error-500.vue"));
const ErrorTemplate = is404 ? _Error404 : _Error;
</script>

View file

@ -0,0 +1,7 @@
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
error: ObjectConstructor;
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
error: ObjectConstructor;
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,70 @@
import type { PropType, RendererNode, VNode } from 'vue';
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
name: {
type: StringConstructor;
required: true;
};
lazy: BooleanConstructor;
props: {
type: ObjectConstructor;
default: () => undefined;
};
context: {
type: ObjectConstructor;
default: () => {};
};
scopeId: {
type: PropType<string | undefined | null>;
default: () => undefined;
};
source: {
type: StringConstructor;
default: () => undefined;
};
dangerouslyLoadClientComponents: {
type: BooleanConstructor;
default: boolean;
};
}>, (_ctx: any, _cache: any) => (VNode<RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | VNode<RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[])[] | VNode<any, any, {
[key: string]: any;
}>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "error"[], "error", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
name: {
type: StringConstructor;
required: true;
};
lazy: BooleanConstructor;
props: {
type: ObjectConstructor;
default: () => undefined;
};
context: {
type: ObjectConstructor;
default: () => {};
};
scopeId: {
type: PropType<string | undefined | null>;
default: () => undefined;
};
source: {
type: StringConstructor;
default: () => undefined;
};
dangerouslyLoadClientComponents: {
type: BooleanConstructor;
default: boolean;
};
}>> & Readonly<{
onError?: ((...args: any[]) => any) | undefined;
}>, {
props: Record<string, any>;
source: string;
scopeId: string | null | undefined;
lazy: boolean;
context: Record<string, any>;
dangerouslyLoadClientComponents: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,331 @@
import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, toRaw, watch, withMemo } from "vue";
import { debounce } from "perfect-debounce";
import { hash } from "ohash";
import { appendResponseHeader } from "h3";
import { randomUUID } from "uncrypto";
import { joinURL, withQuery } from "ufo";
import { useNuxtApp, useRuntimeConfig } from "../nuxt.js";
import { createError } from "../composables/error.js";
import { prerenderRoutes, useRequestEvent } from "../composables/ssr.js";
import { injectHead } from "../composables/head.js";
import { getFragmentHTML, isEndFragment, isStartFragment } from "./utils.js";
import { appBaseURL, remoteComponentIslands, selectiveClient } from "#build/nuxt.config.mjs";
const pKey = "_islandPromises";
const SSR_UID_RE = /data-island-uid="([^"]*)"/;
const DATA_ISLAND_UID_RE = /data-island-uid(="")?(?!="[^"])/g;
const SLOTNAME_RE = /data-island-slot="([^"]*)"/g;
const SLOT_FALLBACK_RE = / data-island-slot="([^"]*)"[^>]*>/g;
const ISLAND_SCOPE_ID_RE = /^<[^> ]*/;
let id = 1;
const getId = import.meta.client ? () => (id++).toString() : randomUUID;
const components = import.meta.client ? /* @__PURE__ */ new Map() : void 0;
async function loadComponents(source = appBaseURL, paths) {
if (!paths) {
return;
}
const promises = [];
for (const [component, item] of Object.entries(paths)) {
if (!components.has(component)) {
promises.push((async () => {
const chunkSource = joinURL(source, item.chunk);
const c = await import(
/* @vite-ignore */
chunkSource
).then((m) => m.default || m);
components.set(component, c);
})());
}
}
await Promise.all(promises);
}
export default defineComponent({
name: "NuxtIsland",
inheritAttrs: false,
props: {
name: {
type: String,
required: true
},
lazy: Boolean,
props: {
type: Object,
default: () => void 0
},
context: {
type: Object,
default: () => ({})
},
scopeId: {
type: String,
default: () => void 0
},
source: {
type: String,
default: () => void 0
},
dangerouslyLoadClientComponents: {
type: Boolean,
default: false
}
},
emits: ["error"],
async setup(props, { slots, expose, emit }) {
let canTeleport = import.meta.server;
const teleportKey = shallowRef(0);
const key = shallowRef(0);
const canLoadClientComponent = computed(() => selectiveClient && (props.dangerouslyLoadClientComponents || !props.source));
const error = ref(null);
const config = useRuntimeConfig();
const nuxtApp = useNuxtApp();
const filteredProps = computed(() => props.props ? Object.fromEntries(Object.entries(props.props).filter(([key2]) => !key2.startsWith("data-v-"))) : {});
const hashId = computed(() => hash([props.name, filteredProps.value, props.context, props.source]).replace(/[-_]/g, ""));
const instance = getCurrentInstance();
const event = useRequestEvent();
let activeHead;
const eventFetch = import.meta.server ? event.fetch : globalThis.fetch;
const mounted = shallowRef(false);
onMounted(() => {
mounted.value = true;
teleportKey.value++;
});
onBeforeUnmount(() => {
if (activeHead) {
activeHead.dispose();
}
});
function setPayload(key2, result) {
const toRevive = {};
if (result.props) {
toRevive.props = result.props;
}
if (result.slots) {
toRevive.slots = result.slots;
}
if (result.components) {
toRevive.components = result.components;
}
if (result.head) {
toRevive.head = result.head;
}
nuxtApp.payload.data[key2] = {
__nuxt_island: {
key: key2,
...import.meta.server && import.meta.prerender ? {} : { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : void 0 } },
result: toRevive
},
...result
};
}
const payloads = {};
if (instance.vnode.el) {
const slots2 = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots;
if (slots2) {
payloads.slots = slots2;
}
if (selectiveClient) {
const components2 = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components;
if (components2) {
payloads.components = components2;
}
}
}
const ssrHTML = ref("");
if (import.meta.client && instance.vnode?.el) {
if (import.meta.dev) {
let currentEl = instance.vnode.el;
let startEl = null;
let isFirstElement = true;
while (currentEl) {
if (isEndFragment(currentEl)) {
if (startEl !== currentEl.previousSibling) {
console.warn(`[\`Server components(and islands)\`] "${props.name}" must have a single root element. (HTML comments are considered elements as well.)`);
}
break;
} else if (!isStartFragment(currentEl) && isFirstElement) {
isFirstElement = false;
if (currentEl.nodeType === 1) {
startEl = currentEl;
}
}
currentEl = currentEl.nextSibling;
}
}
ssrHTML.value = getFragmentHTML(instance.vnode.el, true)?.join("") || "";
const key2 = `${props.name}_${hashId.value}`;
nuxtApp.payload.data[key2] ||= {};
nuxtApp.payload.data[key2].html = ssrHTML.value.replaceAll(new RegExp(`data-island-uid="${ssrHTML.value.match(SSR_UID_RE)?.[1] || ""}"`, "g"), `data-island-uid=""`);
}
const uid = ref(ssrHTML.value.match(SSR_UID_RE)?.[1] || getId());
const currentSlots = new Set(Object.keys(slots));
const availableSlots = computed(() => new Set([...ssrHTML.value.matchAll(SLOTNAME_RE)].map((m) => m[1])));
const html = computed(() => {
let html2 = ssrHTML.value;
if (props.scopeId) {
html2 = html2.replace(ISLAND_SCOPE_ID_RE, (full) => full + " " + props.scopeId);
}
if (import.meta.client && !canLoadClientComponent.value) {
for (const [key2, value] of Object.entries(payloads.components || {})) {
html2 = html2.replace(new RegExp(` data-island-uid="${uid.value}" data-island-component="${key2}"[^>]*>`), (full) => {
return full + value.html;
});
}
}
if (payloads.slots) {
return html2.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
if (!currentSlots.has(slotName)) {
return full + (payloads.slots?.[slotName]?.fallback || "");
}
return full;
});
}
return html2;
});
const head = injectHead();
async function _fetchComponent(force = false) {
const key2 = `${props.name}_${hashId.value}`;
if (!force && nuxtApp.payload.data[key2]?.html) {
return nuxtApp.payload.data[key2];
}
const url = remoteComponentIslands && props.source ? joinURL(props.source, `/__nuxt_island/${key2}.json`) : `/__nuxt_island/${key2}.json`;
if (import.meta.server && import.meta.prerender) {
nuxtApp.runWithContext(() => prerenderRoutes(url));
}
const r = await eventFetch(withQuery(import.meta.dev && import.meta.client || props.source ? url : joinURL(config.app.baseURL ?? "", url), {
...props.context,
props: props.props ? JSON.stringify(props.props) : void 0
}));
if (!r.ok) {
throw createError({ statusCode: r.status, statusMessage: r.statusText });
}
try {
const result = await r.json();
if (import.meta.server && import.meta.prerender) {
const hints = r.headers.get("x-nitro-prerender");
if (hints) {
appendResponseHeader(event, "x-nitro-prerender", hints);
}
}
setPayload(key2, result);
return result;
} catch (e) {
if (r.status !== 200) {
throw new Error(e.toString(), { cause: r });
}
throw e;
}
}
async function fetchComponent(force = false) {
nuxtApp[pKey] ||= {};
nuxtApp[pKey][uid.value] ||= _fetchComponent(force).finally(() => {
delete nuxtApp[pKey][uid.value];
});
try {
const res = await nuxtApp[pKey][uid.value];
ssrHTML.value = res.html.replaceAll(DATA_ISLAND_UID_RE, `data-island-uid="${uid.value}"`);
key.value++;
error.value = null;
payloads.slots = res.slots || {};
payloads.components = res.components || {};
if (selectiveClient && import.meta.client) {
if (canLoadClientComponent.value && res.components) {
await loadComponents(props.source, res.components);
}
}
if (res?.head) {
if (activeHead) {
activeHead.patch(res.head);
} else {
activeHead = head.push(res.head);
}
}
if (import.meta.client) {
nextTick(() => {
canTeleport = true;
teleportKey.value++;
});
}
} catch (e) {
error.value = e;
emit("error", e);
}
}
expose({
refresh: () => fetchComponent(true)
});
if (import.meta.hot) {
import.meta.hot.on(`nuxt-server-component:${props.name}`, () => {
fetchComponent(true);
});
}
if (import.meta.client) {
watch(props, debounce(() => fetchComponent(), 100), { deep: true });
}
if (import.meta.client && !instance.vnode.el && props.lazy) {
fetchComponent();
} else if (import.meta.server || !instance.vnode.el || !nuxtApp.payload.serverRendered) {
await fetchComponent();
} else if (selectiveClient && canLoadClientComponent.value) {
await loadComponents(props.source, payloads.components);
}
return (_ctx, _cache) => {
if (!html.value || error.value) {
return [slots.fallback?.({ error: error.value }) ?? createVNode("div")];
}
return [
withMemo([key.value], () => {
return createVNode(Fragment, { key: key.value }, [h(createStaticVNode(html.value || "<div></div>", 1))]);
}, _cache, 0),
// should away be triggered ONE tick after re-rendering the static node
withMemo([teleportKey.value], () => {
const teleports = [];
const isKeyOdd = teleportKey.value === 0 || !!(teleportKey.value && !(teleportKey.value % 2));
if (uid.value && html.value && (import.meta.server || props.lazy ? canTeleport : mounted.value || instance.vnode?.el)) {
for (const slot in slots) {
if (availableSlots.value.has(slot)) {
teleports.push(
createVNode(
Teleport,
// use different selectors for even and odd teleportKey to force trigger the teleport
{ to: import.meta.client ? `${isKeyOdd ? "div" : ""}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` },
{ default: () => (payloads.slots?.[slot]?.props?.length ? payloads.slots[slot].props : [{}]).map((data) => slots[slot]?.(data)) }
)
);
}
}
if (selectiveClient) {
if (import.meta.server) {
if (payloads.components) {
for (const [id2, info] of Object.entries(payloads.components)) {
const { html: html2, slots: slots2 } = info;
let replaced = html2.replaceAll("data-island-uid", `data-island-uid="${uid.value}"`);
for (const slot in slots2) {
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, (full) => full + slots2[slot]);
}
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id2}` }, {
default: () => [createStaticVNode(replaced, 1)]
}));
}
}
} else if (canLoadClientComponent.value && payloads.components) {
for (const [id2, info] of Object.entries(payloads.components)) {
const { props: props2, slots: slots2 } = info;
const component = components.get(id2);
const vnode = createVNode(Teleport, { to: `${isKeyOdd ? "div" : ""}[data-island-uid='${uid.value}'][data-island-component="${id2}"]` }, {
default: () => {
return [h(component, props2, Object.fromEntries(Object.entries(slots2 || {}).map(([k, v]) => [
k,
() => createStaticVNode(`<div style="display: contents" data-island-uid data-island-slot="${k}">${v}</div>`, 1)
])))];
}
});
teleports.push(vnode);
}
}
}
}
return h(Fragment, teleports);
}, _cache, 1)
];
};
}
});

View file

@ -0,0 +1,14 @@
import type { DefineComponent, ExtractPublicPropTypes, MaybeRef, PropType } from 'vue';
import type { PageMeta } from '../../pages/runtime/composables.js';
declare const nuxtLayoutProps: {
name: {
type: PropType<unknown extends PageMeta["layout"] ? MaybeRef<string | false> : PageMeta["layout"]>;
default: null;
};
fallback: {
type: PropType<unknown extends PageMeta["layout"] ? MaybeRef<string> : PageMeta["layout"]>;
default: null;
};
};
declare const _default: DefineComponent<ExtractPublicPropTypes<typeof nuxtLayoutProps>>;
export default _default;

View file

@ -0,0 +1,171 @@
import { Suspense, computed, defineComponent, h, inject, mergeProps, nextTick, onMounted, provide, shallowReactive, shallowRef, unref } from "vue";
import { useRoute, useRouter } from "../composables/router.js";
import { useNuxtApp } from "../nuxt.js";
import { _wrapInTransition } from "./utils.js";
import { LayoutMetaSymbol, PageRouteSymbol } from "./injections.js";
import { useRoute as useVueRouterRoute } from "#build/pages";
import layouts from "#build/layouts";
import { appLayoutTransition as defaultLayoutTransition } from "#build/nuxt.config.mjs";
const LayoutLoader = defineComponent({
name: "LayoutLoader",
inheritAttrs: false,
props: {
name: String,
layoutProps: Object
},
setup(props, context) {
return () => h(layouts[props.name], props.layoutProps, context.slots);
}
});
const nuxtLayoutProps = {
name: {
type: [String, Boolean, Object],
default: null
},
fallback: {
type: [String, Object],
default: null
}
};
export default defineComponent({
name: "NuxtLayout",
inheritAttrs: false,
props: nuxtLayoutProps,
setup(props, context) {
const nuxtApp = useNuxtApp();
const injectedRoute = inject(PageRouteSymbol);
const shouldUseEagerRoute = !injectedRoute || injectedRoute === useRoute();
const route = shouldUseEagerRoute ? useVueRouterRoute() : injectedRoute;
const layout = computed(() => {
let layout2 = unref(props.name) ?? route?.meta.layout ?? "default";
if (layout2 && !(layout2 in layouts)) {
if (import.meta.dev && layout2 !== "default") {
console.warn(`Invalid layout \`${layout2}\` selected.`);
}
if (props.fallback) {
layout2 = unref(props.fallback);
}
}
return layout2;
});
const layoutRef = shallowRef();
context.expose({ layoutRef });
const done = nuxtApp.deferHydration();
if (import.meta.client && nuxtApp.isHydrating) {
const removeErrorHook = nuxtApp.hooks.hookOnce("app:error", done);
useRouter().beforeEach(removeErrorHook);
}
if (import.meta.dev) {
nuxtApp._isNuxtLayoutUsed = true;
}
let lastLayout;
return () => {
const hasLayout = layout.value && layout.value in layouts;
const transitionProps = route?.meta.layoutTransition ?? defaultLayoutTransition;
const previouslyRenderedLayout = lastLayout;
lastLayout = layout.value;
return _wrapInTransition(hasLayout && transitionProps, {
default: () => h(Suspense, { suspensible: true, onResolve: () => {
nextTick(done);
} }, {
default: () => h(
LayoutProvider,
{
layoutProps: mergeProps(context.attrs, { ref: layoutRef }),
key: layout.value || void 0,
name: layout.value,
shouldProvide: !props.name,
isRenderingNewLayout: (name) => {
return name !== previouslyRenderedLayout && name === layout.value;
},
hasTransition: !!transitionProps
},
context.slots
)
})
}).default();
};
}
});
const LayoutProvider = defineComponent({
name: "NuxtLayoutProvider",
inheritAttrs: false,
props: {
name: {
type: [String, Boolean]
},
layoutProps: {
type: Object
},
hasTransition: {
type: Boolean
},
shouldProvide: {
type: Boolean
},
isRenderingNewLayout: {
type: Function,
required: true
}
},
setup(props, context) {
const name = props.name;
if (props.shouldProvide) {
provide(LayoutMetaSymbol, {
isCurrent: (route) => name === (route.meta.layout ?? "default")
});
}
const injectedRoute = inject(PageRouteSymbol);
const isNotWithinNuxtPage = injectedRoute && injectedRoute === useRoute();
if (isNotWithinNuxtPage) {
const vueRouterRoute = useVueRouterRoute();
const reactiveChildRoute = {};
for (const _key in vueRouterRoute) {
const key = _key;
Object.defineProperty(reactiveChildRoute, key, {
enumerable: true,
get: () => {
return props.isRenderingNewLayout(props.name) ? vueRouterRoute[key] : injectedRoute[key];
}
});
}
provide(PageRouteSymbol, shallowReactive(reactiveChildRoute));
}
let vnode;
if (import.meta.dev && import.meta.client) {
onMounted(() => {
nextTick(() => {
if (["#comment", "#text"].includes(vnode?.el?.nodeName)) {
if (name) {
console.warn(`[nuxt] \`${name}\` layout does not have a single root node and will cause errors when navigating between routes.`);
} else {
console.warn("[nuxt] `<NuxtLayout>` needs to be passed a single root node in its default slot.");
}
}
});
});
}
return () => {
if (!name || typeof name === "string" && !(name in layouts)) {
if (import.meta.dev && import.meta.client && props.hasTransition) {
vnode = context.slots.default?.();
return vnode;
}
return context.slots.default?.();
}
if (import.meta.dev && import.meta.client && props.hasTransition) {
vnode = h(
LayoutLoader,
{ key: name, layoutProps: props.layoutProps, name },
context.slots
);
return vnode;
}
return h(
LayoutLoader,
{ key: name, layoutProps: props.layoutProps, name },
context.slots
);
};
}
});

View file

@ -0,0 +1,101 @@
import type { AllowedComponentProps, AnchorHTMLAttributes, DefineSetupFnComponent, SlotsType, UnwrapRef, VNode, VNodeProps } from 'vue';
import type { RouteLocation, RouteLocationRaw, RouterLinkProps, UseLinkReturn } from 'vue-router';
import type { NuxtApp } from '../nuxt.js';
/**
* `<NuxtLink>` is a drop-in replacement for both Vue Router's `<RouterLink>` component and HTML's `<a>` tag.
* @see https://nuxt.com/docs/api/components/nuxt-link
*/
export interface NuxtLinkProps<CustomProp extends boolean = false> extends Omit<RouterLinkProps, 'to'> {
custom?: CustomProp;
/**
* Route Location the link should navigate to when clicked on.
*/
to?: RouteLocationRaw;
/**
* An alias for `to`. If used with `to`, `href` will be ignored
*/
href?: NuxtLinkProps['to'];
/**
* Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases
*/
external?: boolean;
/**
* Where to display the linked URL, as the name for a browsing context.
*/
target?: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null;
/**
* A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links.
*/
rel?: 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | (string & {}) | null;
/**
* If set to true, no rel attribute will be added to the link
*/
noRel?: boolean;
/**
* A class to apply to links that have been prefetched.
*/
prefetchedClass?: string;
/**
* When enabled will prefetch middleware, layouts and payloads of links in the viewport.
*/
prefetch?: boolean;
/**
* Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
*/
prefetchOn?: 'visibility' | 'interaction' | Partial<{
visibility: boolean;
interaction: boolean;
}>;
/**
* Escape hatch to disable `prefetch` attribute.
*/
noPrefetch?: boolean;
/**
* An option to either add or remove trailing slashes in the `href` for this specific link.
* Overrides the global `trailingSlash` option if provided.
*/
trailingSlash?: 'append' | 'remove';
}
/**
* Create a NuxtLink component with given options as defaults.
* @see https://nuxt.com/docs/api/components/nuxt-link
*/
export interface NuxtLinkOptions extends Partial<Pick<RouterLinkProps, 'activeClass' | 'exactActiveClass'>>, Partial<Pick<NuxtLinkProps, 'prefetch' | 'prefetchedClass'>> {
/**
* The name of the component.
* @default "NuxtLink"
*/
componentName?: string;
/**
* A default `rel` attribute value applied on external links. Defaults to `"noopener noreferrer"`. Set it to `""` to disable.
*/
externalRelAttribute?: string | null;
/**
* An option to either add or remove trailing slashes in the `href`.
* If unset or not matching the valid values `append` or `remove`, it will be ignored.
*/
trailingSlash?: 'append' | 'remove';
/**
* Allows controlling default setting for when to prefetch links. By default, prefetch is triggered only on visibility.
*/
prefetchOn?: Exclude<NuxtLinkProps['prefetchOn'], string>;
}
type NuxtLinkDefaultSlotProps<CustomProp extends boolean = false> = CustomProp extends true ? {
href: string;
navigate: (e?: MouseEvent) => Promise<void>;
prefetch: (nuxtApp?: NuxtApp) => Promise<void>;
route: (RouteLocation & {
href: string;
}) | undefined;
rel: string | null;
target: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null;
isExternal: boolean;
isActive: false;
isExactActive: false;
} : UnwrapRef<UseLinkReturn>;
type NuxtLinkSlots<CustomProp extends boolean = false> = {
default?: (props: NuxtLinkDefaultSlotProps<CustomProp>) => VNode[];
};
export declare function defineNuxtLink(options: NuxtLinkOptions): (new <CustomProp extends boolean = false>(props: NuxtLinkProps<CustomProp> & VNodeProps & AllowedComponentProps & Omit<AnchorHTMLAttributes, keyof NuxtLinkProps<CustomProp>>) => InstanceType<DefineSetupFnComponent<NuxtLinkProps<CustomProp> & VNodeProps & AllowedComponentProps & Omit<AnchorHTMLAttributes, keyof NuxtLinkProps<CustomProp>>, [], SlotsType<NuxtLinkSlots<CustomProp>>>>) & Record<string, any>;
declare const _default: (new <CustomProp extends boolean = false>(props: NuxtLinkProps<CustomProp> & VNodeProps & AllowedComponentProps & Omit<AnchorHTMLAttributes, keyof NuxtLinkProps<CustomProp>>) => InstanceType<DefineSetupFnComponent<NuxtLinkProps<CustomProp> & VNodeProps & AllowedComponentProps & Omit<AnchorHTMLAttributes, keyof NuxtLinkProps<CustomProp>>, [], SlotsType<NuxtLinkSlots<CustomProp>>>>) & Record<string, any>;
export default _default;

View file

@ -0,0 +1,409 @@
import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent, shallowRef } from "vue";
import { hasProtocol, joinURL, parseQuery, withTrailingSlash, withoutTrailingSlash } from "ufo";
import { preloadRouteComponents } from "../composables/preload.js";
import { onNuxtReady } from "../composables/ready.js";
import { navigateTo, resolveRouteObject, useRouter } from "../composables/router.js";
import { useNuxtApp, useRuntimeConfig } from "../nuxt.js";
import { cancelIdleCallback, requestIdleCallback } from "../compat/idle-callback.js";
import { nuxtLinkDefaults } from "#build/nuxt.config.mjs";
import { hashMode } from "#build/router.options";
const firstNonUndefined = (...args) => args.find((arg) => arg !== void 0);
const NuxtLinkDevKeySymbol = Symbol("nuxt-link-dev-key");
// @__NO_SIDE_EFFECTS__
export function defineNuxtLink(options) {
const componentName = options.componentName || "NuxtLink";
function checkPropConflicts(props, main, sub) {
if (import.meta.dev && props[main] !== void 0 && props[sub] !== void 0) {
console.warn(`[${componentName}] \`${main}\` and \`${sub}\` cannot be used together. \`${sub}\` will be ignored.`);
}
}
function isHashLinkWithoutHashMode(link) {
return !hashMode && typeof link === "string" && link.startsWith("#");
}
function resolveTrailingSlashBehavior(to, resolve, trailingSlash) {
const effectiveTrailingSlash = trailingSlash ?? options.trailingSlash;
if (!to || effectiveTrailingSlash !== "append" && effectiveTrailingSlash !== "remove") {
return to;
}
if (typeof to === "string") {
return applyTrailingSlashBehavior(to, effectiveTrailingSlash);
}
const path = "path" in to && to.path !== void 0 ? to.path : resolve(to).path;
const resolvedPath = {
...to,
name: void 0,
// named routes would otherwise always override trailing slash behavior
path: applyTrailingSlashBehavior(path, effectiveTrailingSlash)
};
return resolvedPath;
}
function useNuxtLink(props) {
const router = useRouter();
const config = useRuntimeConfig();
const hasTarget = computed(() => !!props.target && props.target !== "_self");
const isAbsoluteUrl = computed(() => {
const path = props.to || props.href || "";
return typeof path === "string" && hasProtocol(path, { acceptRelative: true });
});
const builtinRouterLink = resolveComponent("RouterLink");
const useBuiltinLink = builtinRouterLink && typeof builtinRouterLink !== "string" ? builtinRouterLink.useLink : void 0;
const isExternal = computed(() => {
if (props.external) {
return true;
}
const path = props.to || props.href || "";
if (typeof path === "object") {
return false;
}
return path === "" || isAbsoluteUrl.value;
});
const to = computed(() => {
checkPropConflicts(props, "to", "href");
const path = props.to || props.href || "";
if (isExternal.value) {
return path;
}
return resolveTrailingSlashBehavior(path, router.resolve, props.trailingSlash);
});
const link = isExternal.value ? void 0 : useBuiltinLink?.({ ...props, to });
const href = computed(() => {
const effectiveTrailingSlash = props.trailingSlash ?? options.trailingSlash;
if (!to.value || isAbsoluteUrl.value || isHashLinkWithoutHashMode(to.value)) {
return to.value;
}
if (isExternal.value) {
const path = typeof to.value === "object" && "path" in to.value ? resolveRouteObject(to.value) : to.value;
const href2 = typeof path === "object" ? router.resolve(path).href : path;
return applyTrailingSlashBehavior(href2, effectiveTrailingSlash);
}
if (typeof to.value === "object") {
return router.resolve(to.value)?.href ?? null;
}
return applyTrailingSlashBehavior(joinURL(config.app.baseURL, to.value), effectiveTrailingSlash);
});
return {
to,
hasTarget,
isAbsoluteUrl,
isExternal,
//
href,
isActive: link?.isActive ?? computed(() => to.value === router.currentRoute.value.path),
isExactActive: link?.isExactActive ?? computed(() => to.value === router.currentRoute.value.path),
route: link?.route ?? computed(() => router.resolve(to.value)),
async navigate(_e) {
await navigateTo(href.value, { replace: props.replace, external: isExternal.value || hasTarget.value });
}
};
}
return defineComponent({
name: componentName,
props: {
// Routing
to: {
type: [String, Object],
default: void 0,
required: false
},
href: {
type: [String, Object],
default: void 0,
required: false
},
// Attributes
target: {
type: String,
default: void 0,
required: false
},
rel: {
type: String,
default: void 0,
required: false
},
noRel: {
type: Boolean,
default: void 0,
required: false
},
// Prefetching
prefetch: {
type: Boolean,
default: void 0,
required: false
},
prefetchOn: {
type: [String, Object],
default: void 0,
required: false
},
noPrefetch: {
type: Boolean,
default: void 0,
required: false
},
// Styling
activeClass: {
type: String,
default: void 0,
required: false
},
exactActiveClass: {
type: String,
default: void 0,
required: false
},
prefetchedClass: {
type: String,
default: void 0,
required: false
},
// Vue Router's `<RouterLink>` additional props
replace: {
type: Boolean,
default: void 0,
required: false
},
ariaCurrentValue: {
type: String,
default: void 0,
required: false
},
// Edge cases handling
external: {
type: Boolean,
default: void 0,
required: false
},
// Slot API
custom: {
type: Boolean,
default: void 0,
required: false
},
// Behavior
trailingSlash: {
type: String,
default: void 0,
required: false
}
},
useLink: useNuxtLink,
setup(props, { slots }) {
const router = useRouter();
const { to, href, navigate, isExternal, hasTarget, isAbsoluteUrl } = useNuxtLink(props);
const prefetched = shallowRef(false);
const el = import.meta.server ? void 0 : ref(null);
const elRef = import.meta.server ? void 0 : (ref2) => {
el.value = props.custom ? ref2?.$el?.nextElementSibling : ref2?.$el;
};
function shouldPrefetch(mode) {
if (import.meta.server) {
return;
}
return !prefetched.value && (typeof props.prefetchOn === "string" ? props.prefetchOn === mode : props.prefetchOn?.[mode] ?? options.prefetchOn?.[mode]) && (props.prefetch ?? options.prefetch) !== false && props.noPrefetch !== true && props.target !== "_blank" && !isSlowConnection();
}
async function prefetch(nuxtApp = useNuxtApp()) {
if (import.meta.server) {
return;
}
if (prefetched.value) {
return;
}
prefetched.value = true;
const path = typeof to.value === "string" ? to.value : isExternal.value ? resolveRouteObject(to.value) : router.resolve(to.value).fullPath;
const normalizedPath = isExternal.value ? new URL(path, window.location.href).href : path;
await Promise.all([
nuxtApp.hooks.callHook("link:prefetch", normalizedPath).catch(() => {
}),
!isExternal.value && !hasTarget.value && preloadRouteComponents(to.value, router).catch(() => {
})
]);
}
if (import.meta.client) {
checkPropConflicts(props, "noPrefetch", "prefetch");
if (shouldPrefetch("visibility")) {
const nuxtApp = useNuxtApp();
let idleId;
let unobserve = null;
onMounted(() => {
const observer = useObserver();
onNuxtReady(() => {
idleId = requestIdleCallback(() => {
if (el?.value?.tagName) {
unobserve = observer.observe(el.value, async () => {
unobserve?.();
unobserve = null;
await prefetch(nuxtApp);
});
}
});
});
});
onBeforeUnmount(() => {
if (idleId) {
cancelIdleCallback(idleId);
}
unobserve?.();
unobserve = null;
});
}
}
if (import.meta.dev && import.meta.server && !props.custom) {
const isNuxtLinkChild = inject(NuxtLinkDevKeySymbol, false);
if (isNuxtLinkChild) {
console.log("[nuxt] [NuxtLink] You can't nest one <a> inside another <a>. This will cause a hydration error on client-side. You can pass the `custom` prop to take full control of the markup.");
} else {
provide(NuxtLinkDevKeySymbol, true);
}
}
return () => {
if (!isExternal.value && !hasTarget.value && !isHashLinkWithoutHashMode(to.value)) {
const routerLinkProps = {
ref: elRef,
to: to.value,
activeClass: props.activeClass || options.activeClass,
exactActiveClass: props.exactActiveClass || options.exactActiveClass,
replace: props.replace,
ariaCurrentValue: props.ariaCurrentValue,
custom: props.custom
};
if (!props.custom) {
if (import.meta.client) {
if (shouldPrefetch("interaction")) {
routerLinkProps.onPointerenter = prefetch.bind(null, void 0);
routerLinkProps.onFocus = prefetch.bind(null, void 0);
}
if (prefetched.value) {
routerLinkProps.class = props.prefetchedClass || options.prefetchedClass;
}
}
routerLinkProps.rel = props.rel || void 0;
}
return h(
resolveComponent("RouterLink"),
routerLinkProps,
slots.default
);
}
const target = props.target || null;
checkPropConflicts(props, "noRel", "rel");
const rel = firstNonUndefined(
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
props.noRel ? "" : props.rel,
options.externalRelAttribute,
/*
* A fallback rel of `noopener noreferrer` is applied for external links or links that open in a new tab.
* This solves a reverse tabnapping security flaw in browsers pre-2021 as well as improving privacy.
*/
isAbsoluteUrl.value || hasTarget.value ? "noopener noreferrer" : ""
) || null;
if (props.custom) {
if (!slots.default) {
return null;
}
return slots.default({
href: href.value,
navigate,
prefetch,
get route() {
if (!href.value) {
return void 0;
}
const url = new URL(href.value, import.meta.client ? window.location.href : "http://localhost");
return {
path: url.pathname,
fullPath: url.pathname,
get query() {
return parseQuery(url.search);
},
hash: url.hash,
params: {},
name: void 0,
matched: [],
redirectedFrom: void 0,
meta: {},
href: href.value
};
},
rel,
target,
isExternal: isExternal.value || hasTarget.value,
isActive: false,
isExactActive: false
});
}
return h("a", {
ref: el,
href: href.value || null,
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
rel,
target,
onClick: (event) => {
if (isExternal.value || hasTarget.value) {
return;
}
event.preventDefault();
return props.replace ? router.replace(href.value) : router.push(href.value);
}
}, slots.default?.());
};
}
});
}
export default /* @__PURE__ */ defineNuxtLink(nuxtLinkDefaults);
function applyTrailingSlashBehavior(to, trailingSlash) {
const normalizeFn = trailingSlash === "append" ? withTrailingSlash : withoutTrailingSlash;
const hasProtocolDifferentFromHttp = hasProtocol(to) && !to.startsWith("http");
if (hasProtocolDifferentFromHttp) {
return to;
}
return normalizeFn(to, true);
}
function useObserver() {
if (import.meta.server) {
return;
}
const nuxtApp = useNuxtApp();
if (nuxtApp._observer) {
return nuxtApp._observer;
}
let observer = null;
const callbacks = /* @__PURE__ */ new Map();
const observe = (element, callback) => {
observer ||= new IntersectionObserver((entries) => {
for (const entry of entries) {
const callback2 = callbacks.get(entry.target);
const isVisible = entry.isIntersecting || entry.intersectionRatio > 0;
if (isVisible && callback2) {
callback2();
}
}
});
callbacks.set(element, callback);
observer.observe(element);
return () => {
callbacks.delete(element);
observer?.unobserve(element);
if (callbacks.size === 0) {
observer?.disconnect();
observer = null;
}
};
};
const _observer = nuxtApp._observer = {
observe
};
return _observer;
}
const IS_2G_RE = /2g/;
function isSlowConnection() {
if (import.meta.server) {
return;
}
const cn = navigator.connection;
if (cn && (cn.saveData || IS_2G_RE.test(cn.effectiveType))) {
return true;
}
return false;
}

View file

@ -0,0 +1,78 @@
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
throttle: {
type: NumberConstructor;
default: number;
};
duration: {
type: NumberConstructor;
default: number;
};
hideDelay: {
type: NumberConstructor;
default: number;
};
resetDelay: {
type: NumberConstructor;
default: number;
};
height: {
type: NumberConstructor;
default: number;
};
color: {
type: (BooleanConstructor | StringConstructor)[];
default: string;
};
errorColor: {
type: StringConstructor;
default: string;
};
estimatedProgress: {
type: () => (duration: number, elapsed: number) => number;
required: false;
};
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
throttle: {
type: NumberConstructor;
default: number;
};
duration: {
type: NumberConstructor;
default: number;
};
hideDelay: {
type: NumberConstructor;
default: number;
};
resetDelay: {
type: NumberConstructor;
default: number;
};
height: {
type: NumberConstructor;
default: number;
};
color: {
type: (BooleanConstructor | StringConstructor)[];
default: string;
};
errorColor: {
type: StringConstructor;
default: string;
};
estimatedProgress: {
type: () => (duration: number, elapsed: number) => number;
required: false;
};
}>> & Readonly<{}>, {
duration: number;
height: number;
throttle: number;
hideDelay: number;
resetDelay: number;
color: string | boolean;
errorColor: string;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,75 @@
import { defineComponent, h } from "vue";
import { useLoadingIndicator } from "../composables/loading-indicator.js";
export default defineComponent({
name: "NuxtLoadingIndicator",
props: {
throttle: {
type: Number,
default: 200
},
duration: {
type: Number,
default: 2e3
},
hideDelay: {
type: Number,
default: 500
},
resetDelay: {
type: Number,
default: 400
},
height: {
type: Number,
default: 3
},
color: {
type: [String, Boolean],
default: "repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)"
},
errorColor: {
type: String,
default: "repeating-linear-gradient(to right,#f87171 0%,#ef4444 100%)"
},
estimatedProgress: {
type: Function,
required: false
}
},
setup(props, { slots, expose }) {
const { progress, isLoading, error, start, finish, clear } = useLoadingIndicator({
duration: props.duration,
throttle: props.throttle,
hideDelay: props.hideDelay,
resetDelay: props.resetDelay,
estimatedProgress: props.estimatedProgress
});
expose({
progress,
isLoading,
error,
start,
finish,
clear
});
return () => h("div", {
class: "nuxt-loading-indicator",
style: {
position: "fixed",
top: 0,
right: 0,
left: 0,
pointerEvents: "none",
width: "auto",
height: `${props.height}px`,
opacity: isLoading.value ? 1 : 0,
background: error.value ? props.errorColor : props.color || void 0,
backgroundSize: `${progress.value > 0 ? 100 / progress.value * 100 : 0}% auto`,
transform: `scaleX(${progress.value}%)`,
transformOrigin: "left",
transition: "transform 0.1s, height 0.4s, opacity 0.4s",
zIndex: 999999
}
}, slots);
}
});

View file

@ -0,0 +1,60 @@
<template>
<Suspense @resolve="onResolve">
<div v-if="abortRender" />
<ErrorComponent
v-else-if="error"
:error="error"
/>
<IslandRenderer
v-else-if="islandContext"
:context="islandContext"
/>
<component
:is="SingleRenderer"
v-else-if="SingleRenderer"
/>
<AppComponent v-else />
</Suspense>
</template>
<script setup>
import { defineAsyncComponent, onErrorCaptured, onServerPrefetch, provide } from "vue";
import { useNuxtApp } from "../nuxt";
import { isNuxtError, showError, useError } from "../composables/error";
import { useRoute, useRouter } from "../composables/router";
import { PageRouteSymbol } from "../components/injections";
import AppComponent from "#build/app-component.mjs";
import ErrorComponent from "#build/error-component.mjs";
import { componentIslands } from "#build/nuxt.config.mjs";
const IslandRenderer = import.meta.server && componentIslands ? defineAsyncComponent(() => import("./island-renderer").then((r) => r.default || r)) : () => null;
const nuxtApp = useNuxtApp();
const onResolve = nuxtApp.deferHydration();
if (import.meta.client && nuxtApp.isHydrating) {
const removeErrorHook = nuxtApp.hooks.hookOnce("app:error", onResolve);
useRouter().beforeEach(removeErrorHook);
}
const url = import.meta.server ? nuxtApp.ssrContext.url : window.location.pathname;
const SingleRenderer = import.meta.test && import.meta.dev && import.meta.server && url.startsWith("/__nuxt_component_test__/") && defineAsyncComponent(() => import("#build/test-component-wrapper.mjs").then((r) => r.default(import.meta.server ? url : window.location.href)));
provide(PageRouteSymbol, useRoute());
const results = nuxtApp.hooks.callHookWith((hooks) => hooks.map((hook) => hook()), "vue:setup");
if (import.meta.dev && results && results.some((i) => i && "then" in i)) {
console.error("[nuxt] Error in `vue:setup`. Callbacks must be synchronous.");
}
const error = useError();
const abortRender = import.meta.server && error.value && !nuxtApp.ssrContext.error;
const BOT_RE = /bot\b|chrome-lighthouse|facebookexternalhit|google\b/i;
onErrorCaptured((err, target, info) => {
nuxtApp.hooks.callHook("vue:error", err, target, info).catch((hookError) => console.error("[nuxt] Error in `vue:error` hook", hookError));
if (import.meta.client && BOT_RE.test(navigator.userAgent)) {
nuxtApp.hooks.callHook("app:error", err);
console.error(`[nuxt] Not rendering error page for bot with user agent \`${navigator.userAgent}\`:`, err);
return false;
}
if (import.meta.server || isNuxtError(err) && (err.fatal || err.unhandled)) {
const p = nuxtApp.runWithContext(() => showError(err));
onServerPrefetch(() => p);
return false;
}
});
const islandContext = import.meta.server && nuxtApp.ssrContext.islandContext;
</script>

View file

@ -0,0 +1,3 @@
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,26 @@
import type { Politeness } from 'nuxt/app';
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
atomic: {
type: BooleanConstructor;
default: boolean;
};
politeness: {
type: () => Politeness;
default: string;
};
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
atomic: {
type: BooleanConstructor;
default: boolean;
};
politeness: {
type: () => Politeness;
default: string;
};
}>> & Readonly<{}>, {
politeness: Politeness;
atomic: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,48 @@
import { defineComponent, h } from "vue";
import { useRouteAnnouncer } from "../composables/route-announcer.js";
export default defineComponent({
name: "NuxtRouteAnnouncer",
props: {
atomic: {
type: Boolean,
default: false
},
politeness: {
type: String,
default: "polite"
}
},
setup(props, { slots, expose }) {
const { set, polite, assertive, message, politeness } = useRouteAnnouncer({ politeness: props.politeness });
expose({
set,
polite,
assertive,
message,
politeness
});
return () => h("span", {
class: "nuxt-route-announcer",
style: {
position: "absolute"
}
}, h("span", {
"role": "alert",
"aria-live": politeness.value,
"aria-atomic": props.atomic,
"style": {
"border": "0",
"clip": "rect(0 0 0 0)",
"clip-path": "inset(50%)",
"height": "1px",
"width": "1px",
"overflow": "hidden",
"position": "absolute",
"white-space": "nowrap",
"word-wrap": "normal",
"margin": "-1px",
"padding": "0"
}
}, slots.default ? slots.default({ message: message.value }) : message.value));
}
});

View file

@ -0,0 +1,6 @@
export declare const NuxtImg: {
setup: () => void;
};
export declare const NuxtPicture: {
setup: () => void;
};

View file

@ -0,0 +1,14 @@
import { createError } from "../composables/error.js";
function renderStubMessage(name) {
throw createError({
fatal: true,
statusCode: 500,
statusMessage: `${name} is provided by @nuxt/image. Check your console to install it or run 'npx nuxt module add @nuxt/image'`
});
}
export const NuxtImg = {
setup: () => renderStubMessage("<NuxtImg>")
};
export const NuxtPicture = {
setup: () => renderStubMessage("<NuxtPicture>")
};

View file

@ -0,0 +1,22 @@
import type { InjectionKey } from 'vue';
export declare const NuxtTeleportIslandSymbol: InjectionKey<false | string>;
/**
* component only used with componentsIsland
* this teleport the component in SSR only if it needs to be hydrated on client
*/
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
nuxtClient: {
type: BooleanConstructor;
default: boolean;
};
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
nuxtClient: {
type: BooleanConstructor;
default: boolean;
};
}>> & Readonly<{}>, {
nuxtClient: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,38 @@
import { Teleport, defineComponent, h, inject, provide, useId } from "vue";
import { useNuxtApp } from "../nuxt.js";
import paths from "#build/component-chunk";
import { buildAssetsURL } from "#internal/nuxt/paths";
export const NuxtTeleportIslandSymbol = Symbol("NuxtTeleportIslandComponent");
export default defineComponent({
name: "NuxtTeleportIslandComponent",
inheritAttrs: false,
props: {
nuxtClient: {
type: Boolean,
default: false
}
},
setup(props, { slots }) {
const nuxtApp = useNuxtApp();
const to = useId();
if (!nuxtApp.ssrContext?.islandContext || !props.nuxtClient || inject(NuxtTeleportIslandSymbol, false)) {
return () => slots.default?.();
}
provide(NuxtTeleportIslandSymbol, to);
const islandContext = nuxtApp.ssrContext.islandContext;
return () => {
const slot = slots.default()[0];
const slotType = slot.type;
const name = slotType.__name || slotType.name;
islandContext.components[to] = {
chunk: import.meta.dev ? buildAssetsURL(paths[name]) : paths[name],
props: slot.props || {}
};
return [h("div", {
"style": "display: contents;",
"data-island-uid": "",
"data-island-component": to
}, []), h(Teleport, { to }, slot)];
};
}
});

View file

@ -0,0 +1,32 @@
import type { VNode } from 'vue';
/**
* component only used within islands for slot teleport
*/
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
name: {
type: StringConstructor;
required: true;
};
/**
* must be an array to handle v-for
*/
props: {
type: () => Array<any>;
};
}>, (() => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | undefined) | (() => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[]), {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
name: {
type: StringConstructor;
required: true;
};
/**
* must be an array to handle v-for
*/
props: {
type: () => Array<any>;
};
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,53 @@
import { Teleport, createVNode, defineComponent, h, inject } from "vue";
import { useNuxtApp } from "../nuxt.js";
import { NuxtTeleportIslandSymbol } from "./nuxt-teleport-island-component.js";
export default defineComponent({
name: "NuxtTeleportIslandSlot",
inheritAttrs: false,
props: {
name: {
type: String,
required: true
},
/**
* must be an array to handle v-for
*/
props: {
type: Object
}
},
setup(props, { slots }) {
const nuxtApp = useNuxtApp();
const islandContext = nuxtApp.ssrContext?.islandContext;
if (!islandContext) {
return () => slots.default?.()[0];
}
const componentName = inject(NuxtTeleportIslandSymbol, false);
islandContext.slots[props.name] = {
props: props.props || []
};
return () => {
const vnodes = [];
if (nuxtApp.ssrContext?.islandContext && slots.default) {
vnodes.push(h("div", {
"style": "display: contents;",
"data-island-uid": "",
"data-island-slot": props.name
}, {
// Teleport in slot to not be hydrated client-side with the staticVNode
default: () => [createVNode(Teleport, { to: `island-slot=${componentName};${props.name}` }, slots.default?.())]
}));
} else {
vnodes.push(h("div", {
"style": "display: contents;",
"data-island-uid": "",
"data-island-slot": props.name
}));
}
if (slots.fallback) {
vnodes.push(h(Teleport, { to: `island-fallback=${props.name}` }, slots.fallback()));
}
return vnodes;
};
}
});

View file

@ -0,0 +1,148 @@
<script setup>
import { computed, getCurrentInstance, onBeforeUnmount, ref } from "vue";
import { onPrehydrate } from "../composables/ssr";
import { useNuxtApp } from "../nuxt";
const props = defineProps({
locale: { type: String, required: false },
datetime: { type: [String, Number, Date], required: true },
localeMatcher: { type: String, required: false },
weekday: { type: String, required: false },
era: { type: String, required: false },
year: { type: String, required: false },
month: { type: String, required: false },
day: { type: String, required: false },
hour: { type: String, required: false },
minute: { type: String, required: false },
second: { type: String, required: false },
timeZoneName: { type: String, required: false },
formatMatcher: { type: String, required: false },
hour12: { type: Boolean, required: false, default: void 0 },
timeZone: { type: String, required: false },
calendar: { type: String, required: false },
dayPeriod: { type: String, required: false },
numberingSystem: { type: String, required: false },
dateStyle: { type: String, required: false },
timeStyle: { type: String, required: false },
hourCycle: { type: String, required: false },
relative: { type: Boolean, required: false },
numeric: { type: String, required: false },
relativeStyle: { type: String, required: false },
title: { type: [Boolean, String], required: false }
});
const el = getCurrentInstance()?.vnode.el;
const renderedDate = el?.getAttribute("datetime");
const _locale = el?.getAttribute("data-locale");
const nuxtApp = useNuxtApp();
const date = computed(() => {
const date2 = props.datetime;
if (renderedDate && nuxtApp.isHydrating) {
return new Date(renderedDate);
}
if (!props.datetime) {
return /* @__PURE__ */ new Date();
}
return new Date(date2);
});
const now = ref(import.meta.client && nuxtApp.isHydrating && window._nuxtTimeNow ? new Date(window._nuxtTimeNow) : /* @__PURE__ */ new Date());
if (import.meta.client && props.relative) {
const handler = () => {
now.value = /* @__PURE__ */ new Date();
};
const interval = setInterval(handler, 1e3);
onBeforeUnmount(() => clearInterval(interval));
}
const formatter = computed(() => {
const { locale: propsLocale, relative, relativeStyle, ...rest } = props;
if (relative) {
return new Intl.RelativeTimeFormat(_locale ?? propsLocale, { ...rest, style: relativeStyle });
}
return new Intl.DateTimeFormat(_locale ?? propsLocale, rest);
});
const formattedDate = computed(() => {
if (!props.relative) {
return formatter.value.format(date.value);
}
const diffInSeconds = (date.value.getTime() - now.value.getTime()) / 1e3;
const units = [
{ unit: "second", seconds: 1, threshold: 60 },
// 60 seconds minute
{ unit: "minute", seconds: 60, threshold: 60 },
// 60 minutes hour
{ unit: "hour", seconds: 3600, threshold: 24 },
// 24 hours day
{ unit: "day", seconds: 86400, threshold: 30 },
// ~30 days month
{ unit: "month", seconds: 2592e3, threshold: 12 },
// 12 months year
{ unit: "year", seconds: 31536e3, threshold: Infinity }
];
const { unit, seconds } = units.find(({ seconds: seconds2, threshold }) => Math.abs(diffInSeconds / seconds2) < threshold) || units[units.length - 1];
const value = diffInSeconds / seconds;
return formatter.value.format(Math.round(value), unit);
});
const isoDate = computed(() => date.value.toISOString());
const title = computed(() => props.title === true ? isoDate.value : typeof props.title === "string" ? props.title : void 0);
const dataset = {};
if (import.meta.server) {
for (const prop in props) {
if (prop !== "datetime") {
const value = props?.[prop];
if (value) {
const propInKebabCase = prop.split(/(?=[A-Z])/).join("-");
dataset[`data-${propInKebabCase}`] = props?.[prop];
}
}
}
onPrehydrate((el2) => {
const now2 = window._nuxtTimeNow ||= Date.now();
const toCamelCase = (name, index) => {
if (index > 0) {
return name[0].toUpperCase() + name.slice(1);
}
return name;
};
const date2 = new Date(el2.getAttribute("datetime"));
const options = {};
for (const name of el2.getAttributeNames()) {
if (name.startsWith("data-")) {
let optionName = name.slice(5).split("-").map(toCamelCase).join("");
if (optionName === "relativeStyle") {
optionName = "style";
}
options[optionName] = el2.getAttribute(name);
}
}
if (options.relative) {
const diffInSeconds = (date2.getTime() - now2) / 1e3;
const units = [
{ unit: "second", seconds: 1, threshold: 60 },
// 60 seconds minute
{ unit: "minute", seconds: 60, threshold: 60 },
// 60 minutes hour
{ unit: "hour", seconds: 3600, threshold: 24 },
// 24 hours day
{ unit: "day", seconds: 86400, threshold: 30 },
// ~30 days month
{ unit: "month", seconds: 2592e3, threshold: 12 },
// 12 months year
{ unit: "year", seconds: 31536e3, threshold: Infinity }
];
const { unit, seconds } = units.find(({ seconds: seconds2, threshold }) => Math.abs(diffInSeconds / seconds2) < threshold) || units[units.length - 1];
const value = diffInSeconds / seconds;
const formatter2 = new Intl.RelativeTimeFormat(options.locale, options);
el2.textContent = formatter2.format(Math.round(value), unit);
} else {
const formatter2 = new Intl.DateTimeFormat(options.locale, options);
el2.textContent = formatter2.format(date2);
}
});
}
</script>
<template>
<time
v-bind="dataset"
:datetime="isoDate"
:title="title"
>{{ formattedDate }}</time>
</template>

View file

@ -0,0 +1,37 @@
export interface NuxtTimeProps {
locale?: string;
datetime: string | number | Date;
localeMatcher?: 'best fit' | 'lookup';
weekday?: 'long' | 'short' | 'narrow';
era?: 'long' | 'short' | 'narrow';
year?: 'numeric' | '2-digit';
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
day?: 'numeric' | '2-digit';
hour?: 'numeric' | '2-digit';
minute?: 'numeric' | '2-digit';
second?: 'numeric' | '2-digit';
timeZoneName?: 'short' | 'long' | 'shortOffset' | 'longOffset' | 'shortGeneric' | 'longGeneric';
formatMatcher?: 'best fit' | 'basic';
hour12?: boolean;
timeZone?: string;
calendar?: string;
dayPeriod?: 'narrow' | 'short' | 'long';
numberingSystem?: string;
dateStyle?: 'full' | 'long' | 'medium' | 'short';
timeStyle?: 'full' | 'long' | 'medium' | 'short';
hourCycle?: 'h11' | 'h12' | 'h23' | 'h24';
relative?: boolean;
numeric?: 'always' | 'auto';
relativeStyle?: 'long' | 'short' | 'narrow';
title?: boolean | string;
}
declare global {
interface Window {
_nuxtTimeNow?: number;
}
}
declare const __VLS_export: import("vue").DefineComponent<NuxtTimeProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NuxtTimeProps> & Readonly<{}>, {
hour12: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
declare const _default: typeof __VLS_export;
export default _default;

View file

@ -0,0 +1,48 @@
import type { Ref, VNode } from 'vue';
import type { RouteLocationNormalizedLoaded } from 'vue-router';
export declare const defineRouteProvider: (name?: string) => import("vue").DefineComponent<import("vue").ExtractPropTypes<{
route: {
type: () => RouteLocationNormalizedLoaded;
required: true;
};
vnode: () => VNode;
vnodeRef: () => Ref<any>;
renderKey: StringConstructor;
trackRootNodes: BooleanConstructor;
}>, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
route: {
type: () => RouteLocationNormalizedLoaded;
required: true;
};
vnode: () => VNode;
vnodeRef: () => Ref<any>;
renderKey: StringConstructor;
trackRootNodes: BooleanConstructor;
}>> & Readonly<{}>, {
trackRootNodes: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export declare const RouteProvider: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
route: {
type: () => RouteLocationNormalizedLoaded;
required: true;
};
vnode: () => VNode;
vnodeRef: () => Ref<any>;
renderKey: StringConstructor;
trackRootNodes: BooleanConstructor;
}>, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
route: {
type: () => RouteLocationNormalizedLoaded;
required: true;
};
vnode: () => VNode;
vnodeRef: () => Ref<any>;
renderKey: StringConstructor;
trackRootNodes: BooleanConstructor;
}>> & Readonly<{}>, {
trackRootNodes: boolean;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,49 @@
import { defineComponent, h, nextTick, onMounted, provide, shallowReactive } from "vue";
import { PageRouteSymbol } from "./injections.js";
export const defineRouteProvider = (name = "RouteProvider") => defineComponent({
name,
props: {
route: {
type: Object,
required: true
},
vnode: Object,
vnodeRef: Object,
renderKey: String,
trackRootNodes: Boolean
},
setup(props) {
const previousKey = props.renderKey;
const previousRoute = props.route;
const route = {};
for (const key in props.route) {
Object.defineProperty(route, key, {
get: () => previousKey === props.renderKey ? props.route[key] : previousRoute[key],
enumerable: true
});
}
provide(PageRouteSymbol, shallowReactive(route));
let vnode;
if (import.meta.dev && import.meta.client && props.trackRootNodes) {
onMounted(() => {
nextTick(() => {
if (["#comment", "#text"].includes(vnode?.el?.nodeName)) {
const filename = vnode?.type?.__file;
console.warn(`[nuxt] \`${filename}\` does not have a single root node and will cause errors when navigating between routes.`);
}
});
});
}
return () => {
if (!props.vnode) {
return props.vnode;
}
if (import.meta.dev && import.meta.client) {
vnode = h(props.vnode, { ref: props.vnodeRef });
return vnode;
}
return h(props.vnode, { ref: props.vnodeRef });
};
}
});
export const RouteProvider = defineRouteProvider();

View file

@ -0,0 +1,2 @@
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,7 @@
import { createElementBlock, defineComponent } from "vue";
export default defineComponent({
name: "ServerPlaceholder",
render() {
return createElementBlock("div");
}
});

View file

@ -0,0 +1,4 @@
declare const _default: (url: string) => import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
export default _default;

View file

@ -0,0 +1,27 @@
import { defineComponent, h } from "vue";
import { parseQuery } from "vue-router";
import { resolve } from "pathe";
import destr from "destr";
import { devRootDir } from "#build/nuxt.config.mjs";
export default (url) => defineComponent({
name: "NuxtTestComponentWrapper",
inheritAttrs: false,
async setup(props, { attrs }) {
const query = parseQuery(new URL(url, "http://localhost").search);
const urlProps = query.props ? destr(query.props) : {};
const path = resolve(query.path);
if (!path.startsWith(devRootDir)) {
throw new Error(`[nuxt] Cannot access path outside of project root directory: \`${path}\`.`);
}
const comp = await import(
/* @vite-ignore */
path
).then((r) => r.default);
return () => [
h("div", "Component Test Wrapper for " + path),
h("div", { id: "nuxt-component-root" }, [
h(comp, { ...attrs, ...props, ...urlProps })
])
];
}
});

View file

@ -0,0 +1,48 @@
import type { RendererNode, VNode } from 'vue';
import type { RouteLocationNormalized } from 'vue-router';
/**
* Internal utility
* @private
*/
export declare const _wrapInTransition: (props: any, children: any) => {
default: () => any;
};
/**
* Utility used within router guards
* return true if the route has been changed with a page change during navigation
*/
export declare function isChangingPage(to: RouteLocationNormalized, from: RouteLocationNormalized): boolean;
export type SSRBuffer = SSRBufferItem[] & {
hasAsync?: boolean;
};
export type SSRBufferItem = string | SSRBuffer | Promise<SSRBuffer>;
/**
* create buffer retrieved from @vue/server-renderer
* @see https://github.com/vuejs/core/blob/9617dd4b2abc07a5dc40de6e5b759e851b4d0da1/packages/server-renderer/src/render.ts#L57
* @private
*/
export declare function createBuffer(): {
getBuffer(): SSRBuffer;
push(item: SSRBufferItem): void;
};
/**
* helper for NuxtIsland to generate a correct array for scoped data
*/
export declare function vforToArray(source: any): any[];
/**
* Retrieve the HTML content from an element
* Handles `<!--[-->` Fragment elements
* @param element the element to retrieve the HTML
* @param withoutSlots purge all slots from the HTML string retrieved
* @returns {string[]|undefined} An array of string which represent the content of each element. Use `.join('')` to retrieve a component vnode.el HTML
*/
export declare function getFragmentHTML(element: RendererNode | null, withoutSlots?: boolean): string[] | undefined;
/**
* Return a static vnode from an element
* Default to a div if the element is not found and if a fallback is not provided
* @param el renderer node retrieved from the component internal instance
* @param staticNodeFallback fallback string to use if the element is not found. Must be a valid HTML string
*/
export declare function elToStaticVNode(el: RendererNode | null, staticNodeFallback?: string): VNode;
export declare function isStartFragment(element: RendererNode): boolean;
export declare function isEndFragment(element: RendererNode): boolean;

View file

@ -0,0 +1,126 @@
import { Transition, createStaticVNode, h } from "vue";
import { isString, isPromise, isArray, isObject } from "@vue/shared";
import { START_LOCATION } from "#build/pages";
export const _wrapInTransition = (props, children) => {
return { default: () => import.meta.client && props ? h(Transition, props === true ? {} : props, children) : children.default?.() };
};
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g;
const ROUTE_KEY_SYMBOLS_RE = /(:\w+)[?+*]/g;
const ROUTE_KEY_NORMAL_RE = /:\w+/g;
function generateRouteKey(route) {
const source = route?.meta.key ?? route.path.replace(ROUTE_KEY_PARENTHESES_RE, "$1").replace(ROUTE_KEY_SYMBOLS_RE, "$1").replace(ROUTE_KEY_NORMAL_RE, (r) => route.params[r.slice(1)]?.toString() || "");
return typeof source === "function" ? source(route) : source;
}
export function isChangingPage(to, from) {
if (to === from || from === START_LOCATION) {
return false;
}
if (generateRouteKey(to) !== generateRouteKey(from)) {
return true;
}
const areComponentsSame = to.matched.every(
(comp, index) => comp.components && comp.components.default === from.matched[index]?.components?.default
);
if (areComponentsSame) {
return false;
}
return true;
}
export function createBuffer() {
let appendable = false;
const buffer = [];
return {
getBuffer() {
return buffer;
},
push(item) {
const isStringItem = isString(item);
if (appendable && isStringItem) {
buffer[buffer.length - 1] += item;
} else {
buffer.push(item);
}
appendable = isStringItem;
if (isPromise(item) || isArray(item) && item.hasAsync) {
buffer.hasAsync = true;
}
}
};
}
export function vforToArray(source) {
if (isArray(source)) {
return source;
} else if (isString(source)) {
return source.split("");
} else if (typeof source === "number") {
if (import.meta.dev && !Number.isInteger(source)) {
console.warn(`The v-for range expect an integer value but got ${source}.`);
}
const array = [];
for (let i = 0; i < source; i++) {
array[i] = i;
}
return array;
} else if (isObject(source)) {
if (source[Symbol.iterator]) {
return Array.from(
source,
(item) => item
);
} else {
const keys = Object.keys(source);
const array = new Array(keys.length);
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
array[i] = source[key];
}
return array;
}
}
return [];
}
export function getFragmentHTML(element, withoutSlots = false) {
if (element) {
if (element.nodeName === "#comment" && element.nodeValue === "[") {
return getFragmentChildren(element, [], withoutSlots);
}
if (withoutSlots) {
const clone = element.cloneNode(true);
clone.querySelectorAll("[data-island-slot]").forEach((n) => {
n.innerHTML = "";
});
return [clone.outerHTML];
}
return [element.outerHTML];
}
}
function getFragmentChildren(element, blocks = [], withoutSlots = false) {
if (element && element.nodeName) {
if (isEndFragment(element)) {
return blocks;
} else if (!isStartFragment(element)) {
const clone = element.cloneNode(true);
if (withoutSlots) {
clone.querySelectorAll?.("[data-island-slot]").forEach((n) => {
n.innerHTML = "";
});
}
blocks.push(clone.outerHTML);
}
getFragmentChildren(element.nextSibling, blocks, withoutSlots);
}
return blocks;
}
export function elToStaticVNode(el, staticNodeFallback) {
const fragment = el ? getFragmentHTML(el) : staticNodeFallback ? [staticNodeFallback] : void 0;
if (fragment) {
return createStaticVNode(fragment.join(""), fragment.length);
}
return h("div");
}
export function isStartFragment(element) {
return element.nodeName === "#comment" && element.nodeValue === "[";
}
export function isEndFragment(element) {
return element.nodeName === "#comment" && element.nodeValue === "]";
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,60 @@
declare const _default: typeof __VLS_export;
export default _default;
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
title: {
type: StringConstructor;
default: string;
};
readDocs: {
type: StringConstructor;
default: string;
};
followTwitter: {
type: StringConstructor;
default: string;
};
starGitHub: {
type: StringConstructor;
default: string;
};
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
appName: {
type: StringConstructor;
default: string;
};
version: {
type: StringConstructor;
default: string;
};
title: {
type: StringConstructor;
default: string;
};
readDocs: {
type: StringConstructor;
default: string;
};
followTwitter: {
type: StringConstructor;
default: string;
};
starGitHub: {
type: StringConstructor;
default: string;
};
}>> & Readonly<{}>, {
appName: string;
version: string;
title: string;
readDocs: string;
followTwitter: string;
starGitHub: string;
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;

View file

@ -0,0 +1,2 @@
/** @since 3.8.0 */
export declare function withAsyncContext(fn: () => PromiseLike<unknown>): any;

View file

@ -0,0 +1,7 @@
import { getCurrentInstance, withAsyncContext as withVueAsyncContext } from "vue";
export function withAsyncContext(fn) {
return withVueAsyncContext(() => {
const nuxtApp = getCurrentInstance()?.appContext.app.$nuxt;
return nuxtApp ? nuxtApp.runWithContext(fn) : fn();
});
}

View file

@ -0,0 +1,150 @@
import type { MaybeRefOrGetter, MultiWatchSources, Ref } from 'vue';
import type { DedupeOption, DefaultAsyncDataErrorValue, DefaultAsyncDataValue } from 'nuxt/app/defaults';
import type { NuxtApp } from '../nuxt.js';
import type { NuxtError } from './error.js';
export type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error';
export type _Transform<Input = any, Output = any> = (input: Input) => Output | Promise<Output>;
export type AsyncDataHandler<ResT> = (nuxtApp: NuxtApp, options: {
signal: AbortSignal;
}) => Promise<ResT>;
export type PickFrom<T, K extends Array<string>> = T extends Array<any> ? T : T extends Record<string, any> ? keyof T extends K[number] ? T : K[number] extends never ? T : Pick<T, K[number]> : T;
export type KeysOf<T> = Array<T extends T ? keyof T extends string ? keyof T : never : never>;
export type KeyOfRes<Transform extends _Transform> = KeysOf<ReturnType<Transform>>;
export type { MultiWatchSources };
export type NoInfer<T> = [T][T extends any ? 0 : never];
export type AsyncDataRefreshCause = 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch';
export interface AsyncDataOptions<ResT, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue> {
/**
* Whether to fetch on the server side.
* @default true
*/
server?: boolean;
/**
* Whether to resolve the async function after loading the route, instead of blocking client-side navigation
* @default false
*/
lazy?: boolean;
/**
* a factory function to set the default value of the data, before the async function resolves - useful with the `lazy: true` or `immediate: false` options
*/
default?: () => DefaultT | Ref<DefaultT>;
/**
* Provide a function which returns cached data.
* An `undefined` return value will trigger a fetch.
* Default is `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]` which only caches data when payloadExtraction is enabled.
*/
getCachedData?: (key: string, nuxtApp: NuxtApp, context: {
cause: AsyncDataRefreshCause;
}) => NoInfer<DataT> | undefined;
/**
* A function that can be used to alter handler function result after resolving.
* Do not use it along with the `pick` option.
*/
transform?: _Transform<ResT, DataT>;
/**
* Only pick specified keys in this array from the handler function result.
* Do not use it along with the `transform` option.
*/
pick?: PickKeys;
/**
* Watch reactive sources to auto-refresh when changed
*/
watch?: MultiWatchSources;
/**
* When set to false, will prevent the request from firing immediately
* @default true
*/
immediate?: boolean;
/**
* Return data in a deep ref object (it is true by default). It can be set to false to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
*/
deep?: boolean;
/**
* Avoid fetching the same key more than once at a time
* @default 'cancel'
*/
dedupe?: 'cancel' | 'defer';
/**
* A timeout in milliseconds after which the request will be aborted if it has not resolved yet.
*/
timeout?: number;
}
export interface AsyncDataExecuteOptions {
/**
* Force a refresh, even if there is already a pending request. Previous requests will
* not be cancelled, but their result will not affect the data/pending state - and any
* previously awaited promises will not resolve until this new request resolves.
*
* Instead of using `boolean` values, use `cancel` for `true` and `defer` for `false`.
* Boolean values will be removed in a future release.
*/
dedupe?: DedupeOption;
cause?: AsyncDataRefreshCause;
/** @internal */
cachedData?: any;
signal?: AbortSignal;
timeout?: number;
}
export interface _AsyncData<DataT, ErrorT> {
data: Ref<DataT>;
pending: Ref<boolean>;
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>;
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>;
clear: () => void;
error: Ref<ErrorT | DefaultAsyncDataErrorValue>;
status: Ref<AsyncDataRequestStatus>;
}
export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>>;
/**
* Provides access to data that resolves asynchronously in an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-async-data}
* @since 3.0.0
* @param handler An asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side.
* @param options customize the behavior of useAsyncData
*/
export declare function useAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(handler: AsyncDataHandler<ResT>, options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
export declare function useAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(handler: AsyncDataHandler<ResT>, options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
/**
* Provides access to data that resolves asynchronously in an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-async-data}
* @param key A unique key to ensure that data fetching can be properly de-duplicated across requests.
* @param handler An asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side.
* @param options customize the behavior of useAsyncData
*/
export declare function useAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(key: MaybeRefOrGetter<string>, handler: AsyncDataHandler<ResT>, options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
export declare function useAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(key: MaybeRefOrGetter<string>, handler: AsyncDataHandler<ResT>, options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
/**
* Provides access to data that resolves asynchronously in an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-lazy-async-data}
* @since 3.0.0
* @param handler An asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side.
* @param options customize the behavior of useLazyAsyncData
*/
export declare function useLazyAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(handler: AsyncDataHandler<ResT>, options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
export declare function useLazyAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(handler: AsyncDataHandler<ResT>, options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
/**
* Provides access to data that resolves asynchronously in an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-lazy-async-data}
* @param key A unique key to ensure that data fetching can be properly de-duplicated across requests.
* @param handler An asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side.
* @param options customize the behavior of useLazyAsyncData
*/
export declare function useLazyAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(key: MaybeRefOrGetter<string>, handler: AsyncDataHandler<ResT>, options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
export declare function useLazyAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(key: MaybeRefOrGetter<string>, handler: AsyncDataHandler<ResT>, options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | DefaultAsyncDataErrorValue>;
/** @since 3.1.0 */
export declare function useNuxtData<DataT = any>(key: string): {
data: Ref<DataT | DefaultAsyncDataValue>;
};
/** @since 3.0.0 */
export declare function refreshNuxtData(keys?: string | string[]): Promise<void>;
/** @since 3.0.0 */
export declare function clearNuxtData(keys?: string | string[] | ((key: string) => boolean)): void;
export type CreatedAsyncData<ResT, NuxtErrorDataT = unknown, DataT = ResT, DefaultT = undefined> = Omit<_AsyncData<DataT | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>)>, 'clear' | 'refresh'> & {
_off: () => void;
_hash?: Record<string, string | undefined>;
_default: () => unknown;
_init: boolean;
_deps: number;
_execute: (opts?: AsyncDataExecuteOptions) => Promise<void>;
_abortController?: AbortController;
};

View file

@ -0,0 +1,496 @@
import { computed, getCurrentInstance, getCurrentScope, inject, isShallow, nextTick, onBeforeMount, onScopeDispose, onServerPrefetch, onUnmounted, queuePostFlushCb, ref, shallowRef, toRef, toValue, unref, watch } from "vue";
import { debounce } from "perfect-debounce";
import { hash } from "ohash";
import { useNuxtApp } from "../nuxt.js";
import { getUserCaller, toArray } from "../utils.js";
import { clientOnlySymbol } from "../components/client-only.js";
import { createError } from "./error.js";
import { onNuxtReady } from "./ready.js";
import { asyncDataDefaults, granularCachedData, pendingWhenIdle, purgeCachedData, resetAsyncDataToUndefined } from "#build/nuxt.config.mjs";
const isDefer = (dedupe) => dedupe === "defer" || dedupe === false;
export function useAsyncData(...args) {
const autoKey = typeof args[args.length - 1] === "string" ? args.pop() : void 0;
if (_isAutoKeyNeeded(args[0], args[1])) {
args.unshift(autoKey);
}
let [_key, _handler, options = {}] = args;
let keyChanging = false;
const key = computed(() => toValue(_key));
if (typeof key.value !== "string") {
throw new TypeError("[nuxt] [useAsyncData] key must be a string.");
}
if (typeof _handler !== "function") {
throw new TypeError("[nuxt] [useAsyncData] handler must be a function.");
}
const nuxtApp = useNuxtApp();
options.server ??= true;
options.default ??= getDefault;
options.getCachedData ??= getDefaultCachedData;
options.lazy ??= false;
options.immediate ??= true;
options.deep ??= asyncDataDefaults.deep;
options.dedupe ??= "cancel";
const functionName = options._functionName || "useAsyncData";
if (import.meta.dev && typeof options.dedupe === "boolean") {
console.warn(`[nuxt] \`boolean\` values are deprecated for the \`dedupe\` option of \`${functionName}\` and will be removed in the future. Use 'cancel' or 'defer' instead.`);
}
const currentData = nuxtApp._asyncData[key.value];
if (import.meta.dev && currentData) {
const warnings = [];
const values = createHash(_handler, options);
if (values.handler !== currentData._hash?.handler) {
warnings.push(`different handler`);
}
for (const opt of ["transform", "pick", "getCachedData"]) {
if (values[opt] !== currentData._hash[opt]) {
warnings.push(`different \`${opt}\` option`);
}
}
if (currentData._default.toString() !== options.default.toString()) {
warnings.push(`different \`default\` value`);
}
if (options.deep && isShallow(currentData.data)) {
warnings.push(`mismatching \`deep\` option`);
}
if (warnings.length) {
const caller = getUserCaller();
const explanation = caller ? ` (used at ${caller.source}:${caller.line}:${caller.column})` : "";
console.warn(`[nuxt] [${functionName}] Incompatible options detected for "${key.value}"${explanation}:
${warnings.map((w) => `- ${w}`).join("\n")}
You can use a different key or move the call to a composable to ensure the options are shared across calls.`);
}
}
function createInitialFetch() {
const initialFetchOptions = { cause: "initial", dedupe: options.dedupe };
if (!nuxtApp._asyncData[key.value]?._init) {
initialFetchOptions.cachedData = options.getCachedData(key.value, nuxtApp, { cause: "initial" });
nuxtApp._asyncData[key.value] = createAsyncData(nuxtApp, key.value, _handler, options, initialFetchOptions.cachedData);
}
return () => nuxtApp._asyncData[key.value].execute(initialFetchOptions);
}
const initialFetch = createInitialFetch();
const asyncData = nuxtApp._asyncData[key.value];
asyncData._deps++;
const fetchOnServer = options.server !== false && nuxtApp.payload.serverRendered;
if (import.meta.server && fetchOnServer && options.immediate) {
const promise = initialFetch();
if (getCurrentInstance()) {
onServerPrefetch(() => promise);
} else {
nuxtApp.hook("app:created", async () => {
await promise;
});
}
}
if (import.meta.client) {
let unregister = function(key2) {
const data = nuxtApp._asyncData[key2];
if (data?._deps) {
data._deps--;
if (data._deps === 0) {
data?._off();
}
}
};
const instance = getCurrentInstance();
if (instance && fetchOnServer && options.immediate && !instance.sp) {
instance.sp = [];
}
if (import.meta.dev && !nuxtApp.isHydrating && !nuxtApp._processingMiddleware && (!instance || instance?.isMounted)) {
console.warn(`[nuxt] [${functionName}] Component is already mounted, please use $fetch instead. See https://nuxt.com/docs/getting-started/data-fetching`);
}
if (instance && !instance._nuxtOnBeforeMountCbs) {
instance._nuxtOnBeforeMountCbs = [];
const cbs = instance._nuxtOnBeforeMountCbs;
onBeforeMount(() => {
cbs.forEach((cb) => {
cb();
});
cbs.splice(0, cbs.length);
});
onUnmounted(() => cbs.splice(0, cbs.length));
}
const isWithinClientOnly = instance && (instance._nuxtClientOnly || inject(clientOnlySymbol, false));
if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || asyncData.data.value != null)) {
if (pendingWhenIdle) {
asyncData.pending.value = false;
}
asyncData.status.value = asyncData.error.value ? "error" : "success";
} else if (instance && (!isWithinClientOnly && nuxtApp.payload.serverRendered && nuxtApp.isHydrating || options.lazy) && options.immediate) {
instance._nuxtOnBeforeMountCbs.push(initialFetch);
} else if (options.immediate) {
initialFetch();
}
const hasScope = getCurrentScope();
const unsubKeyWatcher = watch(key, (newKey, oldKey) => {
if ((newKey || oldKey) && newKey !== oldKey) {
keyChanging = true;
const hadData = nuxtApp._asyncData[oldKey]?.data.value !== asyncDataDefaults.value;
const wasRunning = nuxtApp._asyncDataPromises[oldKey] !== void 0;
const initialFetchOptions = { cause: "initial", dedupe: options.dedupe };
if (!nuxtApp._asyncData[newKey]?._init) {
let initialValue;
if (oldKey && hadData) {
initialValue = nuxtApp._asyncData[oldKey].data.value;
} else {
initialValue = options.getCachedData(newKey, nuxtApp, { cause: "initial" });
initialFetchOptions.cachedData = initialValue;
}
nuxtApp._asyncData[newKey] = createAsyncData(nuxtApp, newKey, _handler, options, initialValue);
}
nuxtApp._asyncData[newKey]._deps++;
if (oldKey) {
unregister(oldKey);
}
if (options.immediate || hadData || wasRunning) {
nuxtApp._asyncData[newKey].execute(initialFetchOptions);
}
queuePostFlushCb(() => {
keyChanging = false;
});
}
}, { flush: "sync" });
const unsubParamsWatcher = options.watch ? watch(options.watch, () => {
if (keyChanging) {
return;
}
nuxtApp._asyncData[key.value]?._execute({ cause: "watch", dedupe: options.dedupe });
}) : () => {
};
if (hasScope) {
onScopeDispose(() => {
unsubKeyWatcher();
unsubParamsWatcher();
unregister(key.value);
});
}
}
const asyncReturn = {
data: writableComputedRef(() => nuxtApp._asyncData[key.value]?.data),
pending: writableComputedRef(() => nuxtApp._asyncData[key.value]?.pending),
status: writableComputedRef(() => nuxtApp._asyncData[key.value]?.status),
error: writableComputedRef(() => nuxtApp._asyncData[key.value]?.error),
refresh: (...args2) => {
if (!nuxtApp._asyncData[key.value]?._init) {
const initialFetch2 = createInitialFetch();
return initialFetch2();
}
return nuxtApp._asyncData[key.value].execute(...args2);
},
execute: (...args2) => asyncReturn.refresh(...args2),
clear: () => {
const entry = nuxtApp._asyncData[key.value];
if (entry?._abortController) {
try {
entry._abortController.abort(new DOMException("AsyncData aborted by user.", "AbortError"));
} finally {
entry._abortController = void 0;
}
}
clearNuxtDataByKey(nuxtApp, key.value);
}
};
const asyncDataPromise = Promise.resolve(nuxtApp._asyncDataPromises[key.value]).then(() => asyncReturn);
Object.assign(asyncDataPromise, asyncReturn);
return asyncDataPromise;
}
function writableComputedRef(getter) {
return computed({
get() {
return getter()?.value;
},
set(value) {
const ref2 = getter();
if (ref2) {
ref2.value = value;
}
}
});
}
export function useLazyAsyncData(...args) {
const autoKey = typeof args[args.length - 1] === "string" ? args.pop() : void 0;
if (_isAutoKeyNeeded(args[0], args[1])) {
args.unshift(autoKey);
}
const [key, handler, options = {}] = args;
if (import.meta.dev) {
options._functionName ||= "useLazyAsyncData";
}
return useAsyncData(key, handler, { ...options, lazy: true }, null);
}
function _isAutoKeyNeeded(keyOrFetcher, fetcher) {
if (typeof keyOrFetcher === "string") {
return false;
}
if (typeof keyOrFetcher === "object" && keyOrFetcher !== null) {
return false;
}
if (typeof keyOrFetcher === "function" && typeof fetcher === "function") {
return false;
}
return true;
}
export function useNuxtData(key) {
const nuxtApp = useNuxtApp();
if (!(key in nuxtApp.payload.data)) {
nuxtApp.payload.data[key] = asyncDataDefaults.value;
}
if (nuxtApp._asyncData[key]) {
const data = nuxtApp._asyncData[key];
data._deps++;
if (getCurrentScope()) {
onScopeDispose(() => {
data._deps--;
if (data._deps === 0) {
data?._off();
}
});
}
}
return {
data: computed({
get() {
return nuxtApp._asyncData[key]?.data.value ?? nuxtApp.payload.data[key];
},
set(value) {
if (nuxtApp._asyncData[key]) {
nuxtApp._asyncData[key].data.value = value;
} else {
nuxtApp.payload.data[key] = value;
}
}
})
};
}
export async function refreshNuxtData(keys) {
if (import.meta.server) {
return Promise.resolve();
}
await new Promise((resolve) => onNuxtReady(resolve));
const _keys = keys ? toArray(keys) : void 0;
await useNuxtApp().hooks.callHookParallel("app:data:refresh", _keys);
}
export function clearNuxtData(keys) {
const nuxtApp = useNuxtApp();
const _allKeys = Object.keys(nuxtApp.payload.data);
const _keys = !keys ? _allKeys : typeof keys === "function" ? _allKeys.filter(keys) : toArray(keys);
for (const key of _keys) {
clearNuxtDataByKey(nuxtApp, key);
}
}
function clearNuxtDataByKey(nuxtApp, key) {
if (key in nuxtApp.payload.data) {
nuxtApp.payload.data[key] = void 0;
}
if (key in nuxtApp.payload._errors) {
nuxtApp.payload._errors[key] = asyncDataDefaults.errorValue;
}
if (nuxtApp._asyncData[key]) {
nuxtApp._asyncData[key].data.value = resetAsyncDataToUndefined ? void 0 : unref(nuxtApp._asyncData[key]._default());
nuxtApp._asyncData[key].error.value = asyncDataDefaults.errorValue;
if (pendingWhenIdle) {
nuxtApp._asyncData[key].pending.value = false;
}
nuxtApp._asyncData[key].status.value = "idle";
}
if (key in nuxtApp._asyncDataPromises) {
nuxtApp._asyncDataPromises[key] = void 0;
}
}
function pick(obj, keys) {
const newObj = {};
for (const key of keys) {
newObj[key] = obj[key];
}
return newObj;
}
function createAsyncData(nuxtApp, key, _handler, options, initialCachedData) {
nuxtApp.payload._errors[key] ??= asyncDataDefaults.errorValue;
const hasCustomGetCachedData = options.getCachedData !== getDefaultCachedData;
const handler = import.meta.client || !import.meta.prerender || !nuxtApp.ssrContext?._sharedPrerenderCache ? _handler : (nuxtApp2, options2) => {
const value = nuxtApp2.ssrContext._sharedPrerenderCache.get(key);
if (value) {
return value;
}
const promise = Promise.resolve().then(() => nuxtApp2.runWithContext(() => _handler(nuxtApp2, options2)));
nuxtApp2.ssrContext._sharedPrerenderCache.set(key, promise);
return promise;
};
const _ref = options.deep ? ref : shallowRef;
const hasCachedData = initialCachedData != null;
const unsubRefreshAsyncData = nuxtApp.hook("app:data:refresh", async (keys) => {
if (!keys || keys.includes(key)) {
await asyncData.execute({ cause: "refresh:hook" });
}
});
const asyncData = {
data: _ref(hasCachedData ? initialCachedData : options.default()),
pending: pendingWhenIdle ? shallowRef(!hasCachedData) : computed(() => asyncData.status.value === "pending"),
error: toRef(nuxtApp.payload._errors, key),
status: shallowRef("idle"),
execute: (...args) => {
const [_opts, newValue = void 0] = args;
const opts = _opts && newValue === void 0 && typeof _opts === "object" ? _opts : {};
if (import.meta.dev && newValue !== void 0 && (!_opts || typeof _opts !== "object")) {
console.warn(`[nuxt] [${options._functionName}] Do not pass \`execute\` directly to \`watch\`. Instead, use an inline function, such as \`watch(q, () => execute())\`.`);
}
if (nuxtApp._asyncDataPromises[key]) {
if (isDefer(opts.dedupe ?? options.dedupe)) {
return nuxtApp._asyncDataPromises[key];
}
}
if (granularCachedData || opts.cause === "initial" || nuxtApp.isHydrating) {
const cachedData = "cachedData" in opts ? opts.cachedData : options.getCachedData(key, nuxtApp, { cause: opts.cause ?? "refresh:manual" });
if (cachedData != null) {
nuxtApp.payload.data[key] = asyncData.data.value = cachedData;
asyncData.error.value = asyncDataDefaults.errorValue;
asyncData.status.value = "success";
return Promise.resolve(cachedData);
}
}
if (pendingWhenIdle) {
asyncData.pending.value = true;
}
if (asyncData._abortController) {
asyncData._abortController.abort(new DOMException("AsyncData request cancelled by deduplication", "AbortError"));
}
asyncData._abortController = new AbortController();
asyncData.status.value = "pending";
const cleanupController = new AbortController();
const promise = new Promise(
(resolve, reject) => {
try {
const timeout = opts.timeout ?? options.timeout;
const mergedSignal = mergeAbortSignals([asyncData._abortController?.signal, opts?.signal], cleanupController.signal, timeout);
if (mergedSignal.aborted) {
const reason = mergedSignal.reason;
reject(reason instanceof Error ? reason : new DOMException(String(reason ?? "Aborted"), "AbortError"));
return;
}
mergedSignal.addEventListener("abort", () => {
const reason = mergedSignal.reason;
reject(reason instanceof Error ? reason : new DOMException(String(reason ?? "Aborted"), "AbortError"));
}, { once: true, signal: cleanupController.signal });
return Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject);
} catch (err) {
reject(err);
}
}
).then(async (_result) => {
let result = _result;
if (options.transform) {
result = await options.transform(_result);
}
if (options.pick) {
result = pick(result, options.pick);
}
if (import.meta.dev && import.meta.server && result == null) {
const caller = getUserCaller();
const explanation = caller ? ` (used at ${caller.source}:${caller.line}:${caller.column})` : "";
console.warn(`[nuxt] \`${options._functionName || "useAsyncData"}${explanation}\` must return a value (it should not be \`undefined\` or \`null\`) or the request may be duplicated on the client side.`);
}
nuxtApp.payload.data[key] = result;
asyncData.data.value = result;
asyncData.error.value = asyncDataDefaults.errorValue;
asyncData.status.value = "success";
}).catch((error) => {
if (nuxtApp._asyncDataPromises[key] && nuxtApp._asyncDataPromises[key] !== promise) {
return nuxtApp._asyncDataPromises[key];
}
if (asyncData._abortController?.signal.aborted) {
return nuxtApp._asyncDataPromises[key];
}
if (typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError") {
asyncData.status.value = "idle";
return nuxtApp._asyncDataPromises[key];
}
asyncData.error.value = createError(error);
asyncData.data.value = unref(options.default());
asyncData.status.value = "error";
}).finally(() => {
if (pendingWhenIdle) {
asyncData.pending.value = false;
}
cleanupController.abort();
delete nuxtApp._asyncDataPromises[key];
});
nuxtApp._asyncDataPromises[key] = promise;
return nuxtApp._asyncDataPromises[key];
},
_execute: debounce((...args) => asyncData.execute(...args), 0, { leading: true }),
_default: options.default,
_deps: 0,
_init: true,
_hash: import.meta.dev ? createHash(_handler, options) : void 0,
_off: () => {
unsubRefreshAsyncData();
if (nuxtApp._asyncData[key]?._init) {
nuxtApp._asyncData[key]._init = false;
}
if (purgeCachedData && !hasCustomGetCachedData) {
nextTick(() => {
if (!nuxtApp._asyncData[key]?._init) {
clearNuxtDataByKey(nuxtApp, key);
asyncData.execute = () => Promise.resolve();
asyncData.data.value = asyncDataDefaults.value;
}
});
}
}
};
return asyncData;
}
const getDefault = () => asyncDataDefaults.value;
const getDefaultCachedData = (key, nuxtApp, ctx) => {
if (nuxtApp.isHydrating) {
return nuxtApp.payload.data[key];
}
if (ctx.cause !== "refresh:manual" && ctx.cause !== "refresh:hook") {
return nuxtApp.static.data[key];
}
};
function createHash(_handler, options) {
return {
handler: hash(_handler),
transform: options.transform ? hash(options.transform) : void 0,
pick: options.pick ? hash(options.pick) : void 0,
getCachedData: options.getCachedData ? hash(options.getCachedData) : void 0
};
}
function mergeAbortSignals(signals, cleanupSignal, timeout) {
const list = signals.filter((s) => !!s);
if (typeof timeout === "number" && timeout >= 0) {
const timeoutSignal = AbortSignal.timeout?.(timeout);
if (timeoutSignal) {
list.push(timeoutSignal);
}
}
if (AbortSignal.any) {
return AbortSignal.any(list);
}
const controller = new AbortController();
for (const sig of list) {
if (sig.aborted) {
const reason = sig.reason ?? new DOMException("Aborted", "AbortError");
try {
controller.abort(reason);
} catch {
controller.abort();
}
return controller.signal;
}
}
const onAbort = () => {
const abortedSignal = list.find((s) => s.aborted);
const reason = abortedSignal?.reason ?? new DOMException("Aborted", "AbortError");
try {
controller.abort(reason);
} catch {
controller.abort();
}
};
for (const sig of list) {
sig.addEventListener?.("abort", onAbort, { once: true, signal: cleanupSignal });
}
return controller.signal;
}

View file

@ -0,0 +1,25 @@
export interface ReloadNuxtAppOptions {
/**
* Number of milliseconds in which to ignore future reload requests
* @default {10000}
*/
ttl?: number;
/**
* Force a reload even if one has occurred within the previously specified TTL.
* @default {false}
*/
force?: boolean;
/**
* Whether to dump the current Nuxt state to sessionStorage (as `nuxt:reload:state`).
* @default {false}
*/
persistState?: boolean;
/**
* The path to reload. If this is different from the current window location it will
* trigger a navigation and add an entry in the browser history.
* @default {window.location.pathname}
*/
path?: string;
}
/** @since 3.3.0 */
export declare function reloadNuxtApp(options?: ReloadNuxtAppOptions): void;

View file

@ -0,0 +1,30 @@
import destr from "destr";
import { useNuxtApp } from "../nuxt.js";
export function reloadNuxtApp(options = {}) {
if (import.meta.server) {
return;
}
const path = options.path || window.location.pathname;
let handledPath = {};
try {
handledPath = destr(sessionStorage.getItem("nuxt:reload") || "{}");
} catch {
}
if (options.force || handledPath?.path !== path || handledPath?.expires < Date.now()) {
try {
sessionStorage.setItem("nuxt:reload", JSON.stringify({ path, expires: Date.now() + (options.ttl ?? 1e4) }));
} catch {
}
if (options.persistState) {
try {
sessionStorage.setItem("nuxt:reload:state", JSON.stringify({ state: useNuxtApp().payload.state }));
} catch {
}
}
if (window.location.pathname !== path) {
window.location.href = path;
} else {
window.location.reload();
}
}
}

View file

@ -0,0 +1,4 @@
import type { defineComponent } from 'vue';
export declare const NuxtComponentIndicator = "__nuxt_component";
/** @since 3.0.0 */
export declare const defineNuxtComponent: typeof defineComponent;

View file

@ -0,0 +1,82 @@
import { computed, getCurrentInstance } from "vue";
import { hash } from "ohash";
import { getNuxtAppCtx, useNuxtApp } from "../nuxt.js";
import { useHead } from "./head.js";
import { useAsyncData } from "./asyncData.js";
import { useRoute } from "./router.js";
import { createError } from "./error.js";
export const NuxtComponentIndicator = "__nuxt_component";
// @__NO_SIDE_EFFECTS__
function getFetchKey() {
const vm = getCurrentInstance();
const route = useRoute();
const { _fetchKeyBase } = vm.proxy.$options;
return hash([
_fetchKeyBase,
route.path,
route.query,
route.matched.findIndex((r) => Object.values(r.components || {}).includes(vm.type))
]);
}
async function runLegacyAsyncData(res, fn) {
const nuxtApp = useNuxtApp();
const { fetchKey } = getCurrentInstance().proxy.$options;
const key = (typeof fetchKey === "function" ? fetchKey(() => "") : fetchKey) || /* @__PURE__ */ getFetchKey();
const { data, error } = await useAsyncData(`options:asyncdata:${key}`, () => import.meta.server ? nuxtApp.runWithContext(() => fn(nuxtApp)) : fn(nuxtApp));
if (error.value) {
throw createError(error.value);
}
if (data.value && typeof data.value === "object") {
const _res = await res;
for (const key2 in data.value) {
_res[key2] = computed({
get: () => data.value?.[key2],
set(v) {
data.value ||= {};
data.value[key2] = v;
}
});
}
} else if (import.meta.dev) {
console.warn("[nuxt] asyncData should return an object", data);
}
}
export const defineNuxtComponent = /* @__NO_SIDE_EFFECTS__ */ function defineNuxtComponent2(...args) {
const [options, key] = args;
const { setup } = options;
if (!setup && !options.asyncData && !options.head) {
return {
[NuxtComponentIndicator]: true,
...options
};
}
return {
[NuxtComponentIndicator]: true,
_fetchKeyBase: key,
...options,
setup(props, ctx) {
const nuxtApp = useNuxtApp();
let res = {};
if (setup) {
const fn = () => Promise.resolve(setup(props, ctx)).then((r) => r || {});
const nuxtAppCtx = getNuxtAppCtx(nuxtApp._id);
if (import.meta.server) {
res = nuxtAppCtx.callAsync(nuxtApp, fn);
} else {
nuxtAppCtx.set(nuxtApp);
res = fn();
}
}
const promises = [];
if (options.asyncData) {
promises.push(runLegacyAsyncData(res, options.asyncData));
}
if (options.head) {
useHead(typeof options.head === "function" ? () => options.head(nuxtApp) : options.head);
}
return Promise.resolve(res).then(() => Promise.all(promises)).then(() => res).finally(() => {
promises.length = 0;
});
}
};
};

View file

@ -0,0 +1,22 @@
import type { Ref } from 'vue';
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es';
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';
readonly?: boolean;
}
export interface CookieRef<T> extends Ref<T> {
}
/** @since 3.0.0 */
export declare function useCookie<T = string | null | undefined>(name: string, _opts?: CookieOptions<T> & {
readonly?: false;
}): CookieRef<T>;
export declare function useCookie<T = string | null | undefined>(name: string, _opts: CookieOptions<T> & {
readonly: true;
}): Readonly<CookieRef<T>>;
/** @since 3.10.0 */
export declare function refreshCookie(name: string): void;
export {};

View file

@ -0,0 +1,205 @@
import { customRef, getCurrentScope, nextTick, onScopeDispose, ref, watch } from "vue";
import { parse, serialize } from "cookie-es";
import { deleteCookie, getCookie, getRequestHeader, setCookie } from "h3";
import destr from "destr";
import { isEqual } from "ohash";
import { klona } from "klona";
import { useNuxtApp } from "../nuxt.js";
import { useRequestEvent } from "./ssr.js";
import { cookieStore } from "#build/nuxt.config.mjs";
const CookieDefaults = {
path: "/",
watch: true,
decode: (val) => destr(decodeURIComponent(val)),
encode: (val) => encodeURIComponent(typeof val === "string" ? val : JSON.stringify(val))
};
const store = import.meta.client && cookieStore ? globalThis.cookieStore : void 0;
export function useCookie(name, _opts) {
const opts = { ...CookieDefaults, ..._opts };
opts.filter ??= (key) => key === name;
const cookies = readRawCookies(opts) || {};
let delay;
if (opts.maxAge !== void 0) {
delay = opts.maxAge * 1e3;
} else if (opts.expires) {
delay = opts.expires.getTime() - Date.now();
}
const hasExpired = delay !== void 0 && delay <= 0;
const shouldSetInitialClientCookie = import.meta.client && (hasExpired || cookies[name] === void 0 || cookies[name] === null);
const cookieValue = klona(hasExpired ? void 0 : cookies[name] ?? opts.default?.());
const cookie = import.meta.client && delay && !hasExpired ? cookieRef(cookieValue, delay, opts.watch && opts.watch !== "shallow") : ref(cookieValue);
if (import.meta.dev && hasExpired) {
console.warn(`[nuxt] not setting cookie \`${name}\` as it has already expired.`);
}
if (import.meta.client) {
let channel = null;
try {
if (!store && typeof BroadcastChannel !== "undefined") {
channel = new BroadcastChannel(`nuxt:cookies:${name}`);
}
} catch {
}
const callback = (force = false) => {
if (!force) {
if (opts.readonly || isEqual(cookie.value, cookies[name])) {
return;
}
}
writeClientCookie(name, cookie.value, opts);
cookies[name] = klona(cookie.value);
channel?.postMessage({ value: opts.encode(cookie.value) });
};
const handleChange = (data) => {
const value = data.refresh ? readRawCookies(opts)?.[name] : opts.decode(data.value);
watchPaused = true;
cookie.value = value;
cookies[name] = klona(value);
nextTick(() => {
watchPaused = false;
});
};
let watchPaused = false;
const hasScope = !!getCurrentScope();
if (hasScope) {
onScopeDispose(() => {
watchPaused = true;
callback();
channel?.close();
});
}
if (store) {
const changeHandler = (event) => {
const changedCookie = event.changed.find((c) => c.name === name);
const removedCookie = event.deleted.find((c) => c.name === name);
if (changedCookie) {
handleChange({ value: changedCookie.value });
}
if (removedCookie) {
handleChange({ value: null });
}
};
store.addEventListener("change", changeHandler);
if (hasScope) {
onScopeDispose(() => store.removeEventListener("change", changeHandler));
}
} else if (channel) {
channel.onmessage = ({ data }) => handleChange(data);
}
if (opts.watch) {
watch(
cookie,
() => {
if (watchPaused) {
return;
}
callback();
},
{ deep: opts.watch !== "shallow" }
);
}
if (shouldSetInitialClientCookie) {
callback(shouldSetInitialClientCookie);
}
} else if (import.meta.server) {
const nuxtApp = useNuxtApp();
const writeFinalCookieValue = () => {
if (opts.readonly || isEqual(cookie.value, cookies[name])) {
return;
}
nuxtApp._cookies ||= {};
if (name in nuxtApp._cookies) {
if (isEqual(cookie.value, nuxtApp._cookies[name])) {
return;
}
if (import.meta.dev) {
console.warn(`[nuxt] cookie \`${name}\` was previously set to \`${opts.encode(nuxtApp._cookies[name])}\` and is being overridden to \`${opts.encode(cookie.value)}\`. This may cause unexpected issues.`);
}
}
nuxtApp._cookies[name] = cookie.value;
writeServerCookie(useRequestEvent(nuxtApp), name, cookie.value, opts);
};
const unhook = nuxtApp.hooks.hookOnce("app:rendered", writeFinalCookieValue);
nuxtApp.hooks.hookOnce("app:error", () => {
unhook();
return writeFinalCookieValue();
});
}
return cookie;
}
export function refreshCookie(name) {
if (import.meta.server || store || typeof BroadcastChannel === "undefined") {
return;
}
new BroadcastChannel(`nuxt:cookies:${name}`)?.postMessage({ refresh: true });
}
function readRawCookies(opts = {}) {
if (import.meta.server) {
return parse(getRequestHeader(useRequestEvent(), "cookie") || "", opts);
} else if (import.meta.client) {
return parse(document.cookie, opts);
}
}
function serializeCookie(name, value, opts = {}) {
if (value === null || value === void 0) {
return serialize(name, value, { ...opts, maxAge: -1 });
}
return serialize(name, value, opts);
}
function writeClientCookie(name, value, opts = {}) {
if (import.meta.client) {
document.cookie = serializeCookie(name, value, opts);
}
}
function writeServerCookie(event, name, value, opts = {}) {
if (event) {
if (value !== null && value !== void 0) {
return setCookie(event, name, value, opts);
}
if (getCookie(event, name) !== void 0) {
return deleteCookie(event, name, opts);
}
}
}
const MAX_TIMEOUT_DELAY = 2147483647;
function cookieRef(value, delay, shouldWatch) {
let timeout;
let unsubscribe;
let elapsed = 0;
const internalRef = shouldWatch ? ref(value) : { value };
if (getCurrentScope()) {
onScopeDispose(() => {
unsubscribe?.();
clearTimeout(timeout);
});
}
return customRef((track, trigger) => {
if (shouldWatch) {
unsubscribe = watch(internalRef, trigger);
}
function createExpirationTimeout() {
elapsed = 0;
clearTimeout(timeout);
const timeRemaining = delay - elapsed;
const timeoutLength = timeRemaining < MAX_TIMEOUT_DELAY ? timeRemaining : MAX_TIMEOUT_DELAY;
timeout = setTimeout(() => {
elapsed += timeoutLength;
if (elapsed < delay) {
return createExpirationTimeout();
}
internalRef.value = void 0;
trigger();
}, timeoutLength);
}
return {
get() {
track();
return internalRef.value;
},
set(newValue) {
createExpirationTimeout();
internalRef.value = newValue;
trigger();
}
};
});
}

View file

@ -0,0 +1,25 @@
import type { H3Error } from 'h3';
import type { Ref } from 'vue';
import type { NuxtPayload } from '../nuxt.js';
export declare const NUXT_ERROR_SIGNATURE = "__nuxt_error";
/** @since 3.0.0 */
export declare const useError: () => Ref<NuxtPayload["error"]>;
export interface NuxtError<DataT = unknown> extends H3Error<DataT> {
error?: true;
}
/** @since 3.0.0 */
export declare const showError: <DataT = unknown>(error: string | Error | (Partial<NuxtError<DataT>> & {
status?: number;
statusText?: string;
})) => NuxtError<DataT>;
/** @since 3.0.0 */
export declare const clearError: (options?: {
redirect?: string;
}) => Promise<void>;
/** @since 3.0.0 */
export declare const isNuxtError: <DataT = unknown>(error: unknown) => error is NuxtError<DataT>;
/** @since 3.0.0 */
export declare const createError: <DataT = unknown>(error: string | Error | (Partial<NuxtError<DataT>> & {
status?: number;
statusText?: string;
})) => NuxtError<DataT>;

View file

@ -0,0 +1,40 @@
import { createError as createH3Error } from "h3";
import { toRef } from "vue";
import { useNuxtApp } from "../nuxt.js";
import { useRouter } from "./router.js";
import { nuxtDefaultErrorValue } from "#build/nuxt.config.mjs";
export const NUXT_ERROR_SIGNATURE = "__nuxt_error";
export const useError = /* @__NO_SIDE_EFFECTS__ */ () => toRef(useNuxtApp().payload, "error");
export const showError = (error) => {
const nuxtError = createError(error);
try {
const error2 = /* @__PURE__ */ useError();
if (import.meta.client) {
const nuxtApp = useNuxtApp();
nuxtApp.hooks.callHook("app:error", nuxtError);
}
error2.value ||= nuxtError;
} catch {
throw nuxtError;
}
return nuxtError;
};
export const clearError = async (options = {}) => {
const nuxtApp = useNuxtApp();
const error = /* @__PURE__ */ useError();
nuxtApp.callHook("app:error:cleared", options);
if (options.redirect) {
await useRouter().replace(options.redirect);
}
error.value = nuxtDefaultErrorValue;
};
export const isNuxtError = (error) => !!error && typeof error === "object" && NUXT_ERROR_SIGNATURE in error;
export const createError = (error) => {
const nuxtError = createH3Error(error);
Object.defineProperty(nuxtError, NUXT_ERROR_SIGNATURE, {
value: true,
configurable: false,
writable: false
});
return nuxtError;
};

View file

@ -0,0 +1,38 @@
import type { FetchError, FetchOptions, ResponseType as _ResponseType } from 'ofetch';
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod as _AvailableRouterMethod } from 'nitropack';
import type { MaybeRefOrGetter, Ref } from 'vue';
import type { DefaultAsyncDataErrorValue, DefaultAsyncDataValue } from 'nuxt/app/defaults';
import type { AsyncData, AsyncDataOptions, KeysOf, MultiWatchSources, PickFrom } from './asyncData.js';
type AvailableRouterMethod<R extends NitroFetchRequest> = _AvailableRouterMethod<R> | Uppercase<_AvailableRouterMethod<R>>;
export type FetchResult<ReqT extends NitroFetchRequest, M extends AvailableRouterMethod<ReqT>> = TypedInternalResponse<ReqT, unknown, Lowercase<M>>;
type ComputedOptions<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Function ? T[K] : ComputedOptions<T[K]> | Ref<T[K]> | T[K];
};
interface NitroFetchOptions<R extends NitroFetchRequest, M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>, DataT = any> extends FetchOptions<_ResponseType, DataT> {
method?: M;
}
type ComputedFetchOptions<R extends NitroFetchRequest, M extends AvailableRouterMethod<R>, DataT = any> = ComputedOptions<NitroFetchOptions<R, M, DataT>>;
export interface UseFetchOptions<ResT, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue, R extends NitroFetchRequest = string & {}, M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>> extends Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'watch'>, Omit<ComputedFetchOptions<R, M, DataT>, 'timeout'> {
key?: MaybeRefOrGetter<string>;
$fetch?: typeof globalThis.$fetch;
watch?: MultiWatchSources | false;
}
/**
* Fetch data from an API endpoint with an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-fetch}
* @since 3.0.0
* @param request The URL to fetch
* @param opts extends $fetch options and useAsyncData options
*/
export declare function useFetch<ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod<ReqT> = ResT extends void ? 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT> : AvailableRouterMethod<ReqT>, _ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT, DataT = _ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | DefaultAsyncDataErrorValue>;
export declare function useFetch<ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod<ReqT> = ResT extends void ? 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT> : AvailableRouterMethod<ReqT>, _ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT, DataT = _ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | DefaultAsyncDataErrorValue>;
/**
* Fetch data from an API endpoint with an SSR-friendly composable.
* See {@link https://nuxt.com/docs/api/composables/use-lazy-fetch}
* @since 3.0.0
* @param request The URL to fetch
* @param opts extends $fetch options and useAsyncData options
*/
export declare function useLazyFetch<ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod<ReqT> = ResT extends void ? 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT> : AvailableRouterMethod<ReqT>, _ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT, DataT = _ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DefaultAsyncDataValue>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: Omit<UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | DefaultAsyncDataErrorValue>;
export declare function useLazyFetch<ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod<ReqT> = ResT extends void ? 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT> : AvailableRouterMethod<ReqT>, _ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT, DataT = _ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = DataT>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: Omit<UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, 'lazy'>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | DefaultAsyncDataErrorValue>;
export {};

View file

@ -0,0 +1,123 @@
import { computed, reactive, toValue, watch } from "vue";
import { hash } from "ohash";
import { isPlainObject } from "@vue/shared";
import { useRequestFetch } from "./ssr.js";
import { useAsyncData } from "./asyncData.js";
import { alwaysRunFetchOnKeyChange, fetchDefaults } from "#build/nuxt.config.mjs";
export function useFetch(request, arg1, arg2) {
const [opts = {}, autoKey] = typeof arg1 === "string" ? [{}, arg1] : [arg1, arg2];
const _request = computed(() => toValue(request));
const key = computed(() => toValue(opts.key) || "$f" + hash([autoKey, typeof _request.value === "string" ? _request.value : "", ...generateOptionSegments(opts)]));
if (!opts.baseURL && typeof _request.value === "string" && (_request.value[0] === "/" && _request.value[1] === "/")) {
throw new Error('[nuxt] [useFetch] the request URL must not start with "//".');
}
const {
server,
lazy,
default: defaultFn,
transform,
pick,
watch: watchSources,
immediate,
getCachedData,
deep,
dedupe,
timeout,
...fetchOptions
} = opts;
const _fetchOptions = reactive({
...fetchDefaults,
...fetchOptions,
cache: typeof opts.cache === "boolean" ? void 0 : opts.cache
});
const _asyncDataOptions = {
server,
lazy,
default: defaultFn,
transform,
pick,
immediate,
getCachedData,
deep,
dedupe,
timeout,
watch: watchSources === false ? [] : [...watchSources || [], _fetchOptions]
};
if (import.meta.dev) {
_asyncDataOptions._functionName ||= "useFetch";
}
if (alwaysRunFetchOnKeyChange && !immediate) {
let setImmediate = function() {
_asyncDataOptions.immediate = true;
};
watch(key, setImmediate, { flush: "sync", once: true });
watch([...watchSources || [], _fetchOptions], setImmediate, { flush: "sync", once: true });
}
const asyncData = useAsyncData(watchSources === false ? key.value : key, (_, { signal }) => {
let _$fetch = opts.$fetch || globalThis.$fetch;
if (import.meta.server && !opts.$fetch) {
const isLocalFetch = typeof _request.value === "string" && _request.value[0] === "/" && (!toValue(opts.baseURL) || toValue(opts.baseURL)[0] === "/");
if (isLocalFetch) {
_$fetch = useRequestFetch();
}
}
return _$fetch(_request.value, { signal, ..._fetchOptions });
}, _asyncDataOptions);
return asyncData;
}
export function useLazyFetch(request, arg1, arg2) {
const [opts = {}, autoKey] = typeof arg1 === "string" ? [{}, arg1] : [arg1, arg2];
if (import.meta.dev) {
opts._functionName ||= "useLazyFetch";
}
return useFetch(
request,
{
...opts,
lazy: true
},
// @ts-expect-error we pass an extra argument with the resolved auto-key to prevent another from being injected
autoKey
);
}
function generateOptionSegments(opts) {
const segments = [
toValue(opts.method)?.toUpperCase() || "GET",
toValue(opts.baseURL)
];
for (const _obj of [opts.query || opts.params]) {
const obj = toValue(_obj);
if (!obj) {
continue;
}
const unwrapped = {};
for (const [key, value] of Object.entries(obj)) {
unwrapped[toValue(key)] = toValue(value);
}
segments.push(unwrapped);
}
if (opts.body) {
const value = toValue(opts.body);
if (!value) {
segments.push(hash(value));
} else if (value instanceof ArrayBuffer) {
segments.push(hash(Object.fromEntries([...new Uint8Array(value).entries()].map(([k, v]) => [k, v.toString()]))));
} else if (value instanceof FormData) {
const obj = {};
for (const entry of value.entries()) {
const [key, val] = entry;
obj[key] = val instanceof File ? val.name : val;
}
segments.push(hash(obj));
} else if (isPlainObject(value)) {
segments.push(hash(reactive(value)));
} else {
try {
segments.push(hash(value));
} catch {
console.warn("[useFetch] Failed to hash body", value);
}
}
}
return segments;
}

View file

@ -0,0 +1 @@
export { injectHead, useHead, useServerHead, useSeoMeta, useServerSeoMeta, useHeadSafe, useServerHeadSafe, } from '#unhead/composables';

View file

@ -0,0 +1,9 @@
export {
injectHead,
useHead,
useServerHead,
useSeoMeta,
useServerSeoMeta,
useHeadSafe,
useServerHeadSafe
} from "#unhead/composables";

View file

@ -0,0 +1,9 @@
import type { NuxtPayload } from '../nuxt.js';
/**
* Allows full control of the hydration cycle to set and receive data from the server.
* @param key a unique key to identify the data in the Nuxt payload
* @param get a function that returns the value to set the initial data
* @param set a function that will receive the data on the client-side
* @since 3.0.0
*/
export declare const useHydration: <K extends keyof NuxtPayload, T = NuxtPayload[K]>(key: K, get: () => T, set: (value: T) => void) => void;

View file

@ -0,0 +1,14 @@
import { useNuxtApp } from "../nuxt.js";
export const useHydration = (key, get, set) => {
const nuxtApp = useNuxtApp();
if (import.meta.server) {
nuxtApp.hooks.hook("app:rendered", () => {
nuxtApp.payload[key] = get();
});
}
if (import.meta.client) {
nuxtApp.hooks.hook("app:created", () => {
set(nuxtApp.payload[key]);
});
}
};

View file

@ -0,0 +1,3 @@
import { useId as _useId } from 'vue';
/** @deprecated Use `useId` from `vue` */
export declare const useId: typeof _useId;

View file

@ -0,0 +1,2 @@
import { useId as _useId } from "vue";
export const useId = _useId;

View file

@ -0,0 +1,29 @@
export { defineNuxtComponent } from './component.js';
export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData } from './asyncData.js';
export type { AsyncDataOptions, AsyncData, AsyncDataRequestStatus } from './asyncData.js';
export { useHydration } from './hydrate.js';
export { callOnce } from './once.js';
export { useState, clearNuxtState } from './state.js';
export { clearError, createError, isNuxtError, showError, useError } from './error.js';
export type { NuxtError } from './error.js';
export { useFetch, useLazyFetch } from './fetch.js';
export type { FetchResult, UseFetchOptions } from './fetch.js';
export { useCookie, refreshCookie } from './cookie.js';
export type { CookieOptions, CookieRef } from './cookie.js';
export { onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader } from './ssr.js';
export { onNuxtReady } from './ready.js';
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router.js';
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router.js';
export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload.js';
export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from './payload.js';
export { getAppManifest, getRouteRules } from './manifest.js';
export type { NuxtAppManifest, NuxtAppManifestMeta } from './manifest.js';
export type { ReloadNuxtAppOptions } from './chunk.js';
export { reloadNuxtApp } from './chunk.js';
export { useRequestURL } from './url.js';
export { usePreviewMode } from './preview.js';
export { useId } from './id.js';
export { useRouteAnnouncer } from './route-announcer.js';
export type { Politeness } from './route-announcer.js';
export { useRuntimeHook } from './runtime-hook.js';
export { injectHead, useHead, useHeadSafe, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta } from './head.js';

View file

@ -0,0 +1,21 @@
export { defineNuxtComponent } from "./component.js";
export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData } from "./asyncData.js";
export { useHydration } from "./hydrate.js";
export { callOnce } from "./once.js";
export { useState, clearNuxtState } from "./state.js";
export { clearError, createError, isNuxtError, showError, useError } from "./error.js";
export { useFetch, useLazyFetch } from "./fetch.js";
export { useCookie, refreshCookie } from "./cookie.js";
export { onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader } from "./ssr.js";
export { onNuxtReady } from "./ready.js";
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from "./router.js";
export { preloadComponents, prefetchComponents, preloadRouteComponents } from "./preload.js";
export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from "./payload.js";
export { getAppManifest, getRouteRules } from "./manifest.js";
export { reloadNuxtApp } from "./chunk.js";
export { useRequestURL } from "./url.js";
export { usePreviewMode } from "./preview.js";
export { useId } from "./id.js";
export { useRouteAnnouncer } from "./route-announcer.js";
export { useRuntimeHook } from "./runtime-hook.js";
export { injectHead, useHead, useHeadSafe, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta } from "./head.js";

View file

@ -0,0 +1,40 @@
import type { AsyncComponentLoader, Component, ComponentPublicInstance, DefineComponent } from 'vue';
type LazyHydrationComponent<T extends Component, Props> = T & DefineComponent<Props, {}, {}, {}, {}, {}, {}, {
hydrated: () => void;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'visible', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateOnVisible?: IntersectionObserverInit | true;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'idle', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateOnIdle?: number | true;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'interaction', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateOnInteraction?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap>;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'mediaQuery', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateOnMediaQuery: string;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'if', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateWhen: boolean;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'time', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateAfter: number | true;
}>;
export declare function defineLazyHydrationComponent<T extends Component = {
new (): ComponentPublicInstance;
}>(strategy: 'never', source: AsyncComponentLoader<T>): LazyHydrationComponent<T, {
hydrateNever?: true;
}>;
export {};

View file

@ -0,0 +1,2 @@
export function defineLazyHydrationComponent(_strategy, _source) {
}

View file

@ -0,0 +1,39 @@
import type { Ref } from 'vue';
export type LoadingIndicatorOpts = {
/** @default 2000 */
duration: number;
/** @default 200 */
throttle: number;
/** @default 500 */
hideDelay: number;
/** @default 400 */
resetDelay: number;
/**
* You can provide a custom function to customize the progress estimation,
* which is a function that receives the duration of the loading bar (above)
* and the elapsed time. It should return a value between 0 and 100.
*/
estimatedProgress?: (duration: number, elapsed: number) => number;
};
export type LoadingIndicator = {
_cleanup: () => void;
progress: Ref<number>;
isLoading: Ref<boolean>;
error: Ref<boolean>;
start: (opts?: {
force?: boolean;
}) => void;
set: (value: number, opts?: {
force?: boolean;
}) => void;
finish: (opts?: {
force?: boolean;
error?: boolean;
}) => void;
clear: () => void;
};
/**
* composable to handle the loading state of the page
* @since 3.9.0
*/
export declare function useLoadingIndicator(opts?: Partial<LoadingIndicatorOpts>): Omit<LoadingIndicator, '_cleanup'>;

View file

@ -0,0 +1,142 @@
import { computed, getCurrentScope, onScopeDispose, shallowRef } from "vue";
import { useNuxtApp } from "../nuxt.js";
function defaultEstimatedProgress(duration, elapsed) {
const completionPercentage = elapsed / duration * 100;
return 2 / Math.PI * 100 * Math.atan(completionPercentage / 50);
}
function createLoadingIndicator(opts = {}) {
const { duration = 2e3, throttle = 200, hideDelay = 500, resetDelay = 400 } = opts;
const getProgress = opts.estimatedProgress || defaultEstimatedProgress;
const nuxtApp = useNuxtApp();
const progress = shallowRef(0);
const isLoading = shallowRef(false);
const error = shallowRef(false);
let done = false;
let rafId;
let throttleTimeout;
let hideTimeout;
let resetTimeout;
const start = (opts2 = {}) => {
_clearTimeouts();
error.value = false;
set(0, opts2);
};
function set(at = 0, opts2 = {}) {
if (nuxtApp.isHydrating) {
return;
}
if (at >= 100) {
return finish({ force: opts2.force });
}
clear();
progress.value = at < 0 ? 0 : at;
const throttleTime = opts2.force ? 0 : throttle;
if (throttleTime && import.meta.client) {
throttleTimeout = setTimeout(() => {
isLoading.value = true;
_startProgress();
}, throttleTime);
} else {
isLoading.value = true;
_startProgress();
}
}
function _hide() {
if (import.meta.client) {
hideTimeout = setTimeout(() => {
isLoading.value = false;
resetTimeout = setTimeout(() => {
progress.value = 0;
}, resetDelay);
}, hideDelay);
}
}
function finish(opts2 = {}) {
progress.value = 100;
done = true;
clear();
_clearTimeouts();
if (opts2.error) {
error.value = true;
}
if (opts2.force) {
progress.value = 0;
isLoading.value = false;
} else {
_hide();
}
}
function _clearTimeouts() {
if (import.meta.client) {
clearTimeout(hideTimeout);
clearTimeout(resetTimeout);
}
}
function clear() {
if (import.meta.client) {
clearTimeout(throttleTimeout);
cancelAnimationFrame(rafId);
}
}
function _startProgress() {
done = false;
let startTimeStamp;
function step(timeStamp) {
if (done) {
return;
}
startTimeStamp ??= timeStamp;
const elapsed = timeStamp - startTimeStamp;
progress.value = Math.max(0, Math.min(100, getProgress(duration, elapsed)));
if (import.meta.client) {
rafId = requestAnimationFrame(step);
}
}
if (import.meta.client) {
rafId = requestAnimationFrame(step);
}
}
let _cleanup = () => {
};
if (import.meta.client) {
const unsubLoadingStartHook = nuxtApp.hook("page:loading:start", () => {
start();
});
const unsubLoadingFinishHook = nuxtApp.hook("page:loading:end", () => {
finish();
});
const unsubError = nuxtApp.hook("vue:error", () => finish());
_cleanup = () => {
unsubError();
unsubLoadingStartHook();
unsubLoadingFinishHook();
clear();
};
}
return {
_cleanup,
progress: computed(() => progress.value),
isLoading: computed(() => isLoading.value),
error: computed(() => error.value),
start,
set,
finish,
clear
};
}
export function useLoadingIndicator(opts = {}) {
const nuxtApp = useNuxtApp();
const indicator = nuxtApp._loadingIndicator ||= createLoadingIndicator(opts);
if (import.meta.client && getCurrentScope()) {
nuxtApp._loadingIndicatorDeps ||= 0;
nuxtApp._loadingIndicatorDeps++;
onScopeDispose(() => {
nuxtApp._loadingIndicatorDeps--;
if (nuxtApp._loadingIndicatorDeps === 0) {
indicator._cleanup();
delete nuxtApp._loadingIndicator;
}
});
}
return indicator;
}

View file

@ -0,0 +1,20 @@
import type { MatcherExport } from 'radix3';
import type { H3Event } from 'h3';
import type { NitroRouteRules } from 'nitropack';
export interface NuxtAppManifestMeta {
id: string;
timestamp: number;
}
export interface NuxtAppManifest extends NuxtAppManifestMeta {
matcher: MatcherExport;
prerendered: string[];
}
/** @since 3.7.4 */
export declare function getAppManifest(): Promise<NuxtAppManifest>;
/** @since 3.7.4 */
export declare function getRouteRules(event: H3Event): Promise<NitroRouteRules>;
export declare function getRouteRules(options: {
path: string;
}): Promise<Record<string, any>>;
/** @deprecated use `getRouteRules({ path })` instead */
export declare function getRouteRules(url: string): Promise<Record<string, any>>;

View file

@ -0,0 +1,55 @@
import { createMatcherFromExport, createRouter as createRadixRouter, toRouteMatcher } from "radix3";
import { defu } from "defu";
import { useNuxtApp, useRuntimeConfig } from "../nuxt.js";
import { appManifest as isAppManifestEnabled } from "#build/nuxt.config.mjs";
import { buildAssetsURL } from "#internal/nuxt/paths";
let manifest;
let matcher;
function fetchManifest() {
if (!isAppManifestEnabled) {
throw new Error("[nuxt] app manifest should be enabled with `experimental.appManifest`");
}
if (import.meta.server) {
manifest = import("#app-manifest");
} else {
manifest = $fetch(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`), {
responseType: "json"
});
}
manifest.then((m) => {
matcher = createMatcherFromExport(m.matcher);
}).catch((e) => {
console.error("[nuxt] Error fetching app manifest.", e);
});
return manifest;
}
export function getAppManifest() {
if (!isAppManifestEnabled) {
throw new Error("[nuxt] app manifest should be enabled with `experimental.appManifest`");
}
if (import.meta.server) {
useNuxtApp().ssrContext._preloadManifest = true;
}
return manifest || fetchManifest();
}
export async function getRouteRules(arg) {
const path = typeof arg === "string" ? arg : arg.path;
if (import.meta.server) {
useNuxtApp().ssrContext._preloadManifest = true;
const _routeRulesMatcher = toRouteMatcher(
createRadixRouter({ routes: useRuntimeConfig().nitro.routeRules })
);
return defu({}, ..._routeRulesMatcher.matchAll(path).reverse());
}
await getAppManifest();
if (!matcher) {
console.error("[nuxt] Error creating app manifest matcher.", matcher);
return {};
}
try {
return defu({}, ...matcher.matchAll(path).reverse());
} catch (e) {
console.error("[nuxt] Error matching route rules.", e);
return {};
}
}

View file

@ -0,0 +1,14 @@
type CallOnceOptions = {
mode?: 'navigation' | 'render';
};
/**
* An SSR-friendly utility to call a method once
* @param key a unique key ensuring the function can be properly de-duplicated across requests
* @param fn a function to call
* @param options Setup the mode, e.g. to re-execute on navigation
* @see https://nuxt.com/docs/api/utils/call-once
* @since 3.9.0
*/
export declare function callOnce(key?: string, fn?: (() => any | Promise<any>), options?: CallOnceOptions): Promise<void>;
export declare function callOnce(fn?: (() => any | Promise<any>), options?: CallOnceOptions): Promise<void>;
export {};

View file

@ -0,0 +1,49 @@
import { useRouter } from "./router.js";
import { useNuxtApp } from "../nuxt.js";
let _isHmrUpdating = false;
export async function callOnce(...args) {
const autoKey = typeof args[args.length - 1] === "string" ? args.pop() : void 0;
if (typeof args[0] !== "string") {
args.unshift(autoKey);
}
const [_key, fn, options] = args;
if (!_key || typeof _key !== "string") {
throw new TypeError("[nuxt] [callOnce] key must be a string: " + _key);
}
if (fn !== void 0 && typeof fn !== "function") {
throw new Error("[nuxt] [callOnce] fn must be a function: " + fn);
}
const nuxtApp = useNuxtApp();
if (options?.mode === "navigation") {
let callback = function() {
nuxtApp.payload.once.delete(_key);
for (const cleanup of cleanups) {
cleanup();
}
};
const cleanups = [];
cleanups.push(nuxtApp.hooks.hook("page:start", callback), useRouter().beforeResolve(callback));
}
if (nuxtApp.payload.once.has(_key)) {
if (!import.meta.dev || !_isHmrUpdating) {
return;
}
}
nuxtApp._once ||= {};
nuxtApp._once[_key] ||= fn() || true;
await nuxtApp._once[_key];
nuxtApp.payload.once.add(_key);
delete nuxtApp._once[_key];
}
if (import.meta.hot) {
import.meta.hot.on("vite:beforeUpdate", (payload) => {
if (payload.updates.some((u) => u.type === "js-update")) {
_isHmrUpdating = true;
}
});
import.meta.hot.on("vite:afterUpdate", (payload) => {
if (payload.updates.some((u) => u.type === "js-update")) {
_isHmrUpdating = false;
}
});
}

View file

@ -0,0 +1,27 @@
import type { NuxtPayload } from '../nuxt.js';
interface LoadPayloadOptions {
fresh?: boolean;
hash?: string;
}
/** @since 3.0.0 */
export declare function loadPayload(url: string, opts?: LoadPayloadOptions): Promise<Record<string, any> | null>;
/** @since 3.0.0 */
export declare function preloadPayload(url: string, opts?: LoadPayloadOptions): Promise<void>;
/** @since 3.0.0 */
export declare function isPrerendered(url?: string): Promise<boolean>;
/** @since 3.4.0 */
export declare function getNuxtClientPayload(): Promise<Partial<NuxtPayload> | null>;
export declare function parsePayload(payload: string): Promise<any>;
/**
* This is an experimental function for configuring passing rich data from server -> client.
* @since 3.4.0
*/
export declare function definePayloadReducer(name: string, reduce: (data: any) => any): void;
/**
* This is an experimental function for configuring passing rich data from server -> client.
*
* This function _must_ be called in a Nuxt plugin that is `unshift`ed to the beginning of the Nuxt plugins array.
* @since 3.4.0
*/
export declare function definePayloadReviver(name: string, revive: (data: any) => any | undefined): void;
export {};

View file

@ -0,0 +1,137 @@
import { hasProtocol, joinURL } from "ufo";
import { parse } from "devalue";
import { getCurrentInstance, onServerPrefetch, reactive } from "vue";
import { useNuxtApp, useRuntimeConfig } from "../nuxt.js";
import { useHead } from "./head.js";
import { useRoute } from "./router.js";
import { getAppManifest, getRouteRules } from "./manifest.js";
import { appId, appManifest, multiApp, payloadExtraction, renderJsonPayloads } from "#build/nuxt.config.mjs";
export async function loadPayload(url, opts = {}) {
if (import.meta.server || !payloadExtraction) {
return null;
}
const shouldLoadPayload = await isPrerendered(url);
if (!shouldLoadPayload) {
return null;
}
const payloadURL = await _getPayloadURL(url, opts);
return await _importPayload(payloadURL) || null;
}
let linkRelType;
function detectLinkRelType() {
if (import.meta.server) {
return "preload";
}
if (linkRelType) {
return linkRelType;
}
const relList = document.createElement("link").relList;
linkRelType = relList && relList.supports && relList.supports("prefetch") ? "prefetch" : "preload";
return linkRelType;
}
export function preloadPayload(url, opts = {}) {
const nuxtApp = useNuxtApp();
const promise = _getPayloadURL(url, opts).then((payloadURL) => {
const link = renderJsonPayloads ? { rel: detectLinkRelType(), as: "fetch", crossorigin: "anonymous", href: payloadURL } : { rel: "modulepreload", crossorigin: "", href: payloadURL };
if (import.meta.server) {
nuxtApp.runWithContext(() => useHead({ link: [link] }));
} else {
const linkEl = document.createElement("link");
for (const key of Object.keys(link)) {
linkEl[key === "crossorigin" ? "crossOrigin" : key] = link[key];
}
document.head.appendChild(linkEl);
return new Promise((resolve, reject) => {
linkEl.addEventListener("load", () => resolve());
linkEl.addEventListener("error", () => reject());
});
}
});
if (import.meta.server) {
onServerPrefetch(() => promise);
}
return promise;
}
const filename = renderJsonPayloads ? "_payload.json" : "_payload.js";
async function _getPayloadURL(url, opts = {}) {
const u = new URL(url, "http://localhost");
if (u.host !== "localhost" || hasProtocol(u.pathname, { acceptRelative: true })) {
throw new Error("Payload URL must not include hostname: " + url);
}
const config = useRuntimeConfig();
const hash = opts.hash || (opts.fresh ? Date.now() : config.app.buildId);
const cdnURL = config.app.cdnURL;
const baseOrCdnURL = cdnURL && await isPrerendered(url) ? cdnURL : config.app.baseURL;
return joinURL(baseOrCdnURL, u.pathname, filename + (hash ? `?${hash}` : ""));
}
async function _importPayload(payloadURL) {
if (import.meta.server || !payloadExtraction) {
return null;
}
const payloadPromise = renderJsonPayloads ? fetch(payloadURL, { cache: "force-cache" }).then((res) => res.text().then(parsePayload)) : import(
/* webpackIgnore: true */
/* @vite-ignore */
payloadURL
).then((r) => r.default || r);
try {
return await payloadPromise;
} catch (err) {
console.warn("[nuxt] Cannot load payload ", payloadURL, err);
}
return null;
}
export async function isPrerendered(url = useRoute().path) {
const nuxtApp = useNuxtApp();
if (!appManifest) {
return !!nuxtApp.payload.prerenderedAt;
}
url = url === "/" ? url : url.replace(/\/$/, "");
const manifest = await getAppManifest();
if (manifest.prerendered.includes(url)) {
return true;
}
return nuxtApp.runWithContext(async () => {
const rules = await getRouteRules({ path: url });
return !!rules.prerender && !rules.redirect;
});
}
let payloadCache = null;
export async function getNuxtClientPayload() {
if (import.meta.server) {
return null;
}
if (payloadCache) {
return payloadCache;
}
const el = multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) : document.getElementById("__NUXT_DATA__");
if (!el) {
return {};
}
const inlineData = await parsePayload(el.textContent || "");
const externalData = el.dataset.src ? await _importPayload(el.dataset.src) : void 0;
payloadCache = {
...inlineData,
...externalData,
...multiApp ? window.__NUXT__?.[appId] : window.__NUXT__
};
if (payloadCache.config?.public) {
payloadCache.config.public = reactive(payloadCache.config.public);
}
return payloadCache;
}
export async function parsePayload(payload) {
return await parse(payload, useNuxtApp()._payloadRevivers);
}
export function definePayloadReducer(name, reduce) {
if (import.meta.server) {
useNuxtApp().ssrContext._payloadReducers[name] = reduce;
}
}
export function definePayloadReviver(name, revive) {
if (import.meta.dev && getCurrentInstance()) {
console.warn("[nuxt] [definePayloadReviver] This function must be called in a Nuxt plugin that is `unshift`ed to the beginning of the Nuxt plugins array.");
}
if (import.meta.client) {
useNuxtApp()._payloadRevivers[name] = revive;
}
}

View file

@ -0,0 +1,20 @@
import type { Component } from 'vue';
import type { RouteLocationRaw, Router } from 'vue-router';
/**
* Preload a component or components that have been globally registered.
* @param components Pascal-cased name or names of components to prefetch
* @since 3.0.0
*/
export declare const preloadComponents: (components: string | string[]) => Promise<void>;
/**
* Prefetch a component or components that have been globally registered.
* @param components Pascal-cased name or names of components to prefetch
* @since 3.0.0
*/
export declare const prefetchComponents: (components: string | string[]) => Promise<void> | undefined;
export declare function _loadAsyncComponent(component: Component): any;
/** @since 3.0.0 */
export declare function preloadRouteComponents(to: RouteLocationRaw, router?: Router & {
_routePreloaded?: Set<string>;
_preloadPromises?: Array<Promise<unknown>>;
}): Promise<void>;

View file

@ -0,0 +1,55 @@
import { useNuxtApp } from "../nuxt.js";
import { toArray } from "../utils.js";
import { useRouter } from "./router.js";
export const preloadComponents = async (components) => {
if (import.meta.server) {
return;
}
const nuxtApp = useNuxtApp();
components = toArray(components);
await Promise.all(components.map((name) => {
const component = nuxtApp.vueApp._context.components[name];
if (component) {
return _loadAsyncComponent(component);
}
}));
};
export const prefetchComponents = (components) => {
if (import.meta.server) {
return;
}
return preloadComponents(components);
};
export function _loadAsyncComponent(component) {
if (component?.__asyncLoader && !component.__asyncResolved) {
return component.__asyncLoader();
}
}
export async function preloadRouteComponents(to, router = useRouter()) {
if (import.meta.server) {
return;
}
const { path, matched } = router.resolve(to);
if (!matched.length) {
return;
}
router._routePreloaded ||= /* @__PURE__ */ new Set();
if (router._routePreloaded.has(path)) {
return;
}
const promises = router._preloadPromises ||= [];
if (promises.length > 4) {
return Promise.all(promises).then(() => preloadRouteComponents(to, router));
}
router._routePreloaded.add(path);
for (const route of matched) {
const component = route.components?.default;
if (typeof component !== "function") {
continue;
}
const promise = Promise.resolve(component()).catch(() => {
}).finally(() => promises.splice(promises.indexOf(promise)));
promises.push(promise);
}
await Promise.all(promises);
}

View file

@ -0,0 +1,38 @@
interface Preview {
enabled: boolean;
state: Record<any, unknown>;
_initialized?: boolean;
}
/**
* Options for configuring preview mode.
*/
interface PreviewModeOptions<S> {
/**
* A function that determines whether preview mode should be enabled based on the current state.
* @param {Record<any, unknown>} state - The state of the preview.
* @returns {boolean} A boolean indicating whether the preview mode is enabled.
*/
shouldEnable?: (state: Preview['state']) => boolean;
/**
* A function that retrieves the current state.
* The `getState` function will append returned values to current state, so be careful not to accidentally overwrite important state.
* @param {Record<any, unknown>} state - The preview state.
* @returns {Record<any, unknown>} The preview state.
*/
getState?: (state: Preview['state']) => S;
/**
* A function to be called when the preview mode is enabled.
*/
onEnable?: () => void;
/**
* A function to be called when the preview mode is disabled.
*/
onDisable?: () => void;
}
type EnteredState = Record<any, unknown> | null | undefined | void;
/** @since 3.11.0 */
export declare function usePreviewMode<S extends EnteredState>(options?: PreviewModeOptions<S>): {
enabled: import("vue").Ref<boolean, boolean>;
state: S extends void ? Preview["state"] : (NonNullable<S> & Preview["state"]);
};
export {};

View file

@ -0,0 +1,61 @@
import { toRef, watch } from "vue";
import { useState } from "./state.js";
import { refreshNuxtData } from "./asyncData.js";
import { useRoute, useRouter } from "./router.js";
let unregisterRefreshHook;
export function usePreviewMode(options = {}) {
const preview = useState("_preview-state", () => ({
enabled: false,
state: {}
}));
if (preview.value._initialized) {
return {
enabled: toRef(preview.value, "enabled"),
state: preview.value.state
};
}
if (import.meta.client) {
preview.value._initialized = true;
}
if (!preview.value.enabled) {
const shouldEnable = options.shouldEnable ?? defaultShouldEnable;
const result = shouldEnable(preview.value.state);
if (typeof result === "boolean") {
preview.value.enabled = result;
}
}
watch(() => preview.value.enabled, (value) => {
if (value) {
const getState = options.getState ?? getDefaultState;
const newState = getState(preview.value.state);
if (newState !== preview.value.state) {
Object.assign(preview.value.state, newState);
}
if (import.meta.client && !unregisterRefreshHook) {
const onEnable = options.onEnable ?? refreshNuxtData;
onEnable();
unregisterRefreshHook = options.onDisable ?? useRouter().afterEach(() => refreshNuxtData());
}
} else if (unregisterRefreshHook) {
unregisterRefreshHook();
unregisterRefreshHook = void 0;
}
}, { immediate: true, flush: "sync" });
return {
enabled: toRef(preview.value, "enabled"),
state: preview.value.state
};
}
function defaultShouldEnable() {
const route = useRoute();
const previewQueryName = "preview";
return route.query[previewQueryName] === "true";
}
function getDefaultState(state) {
if (state.token !== void 0) {
return state;
}
const route = useRoute();
state.token = Array.isArray(route.query.token) ? route.query.token[0] : route.query.token;
return state;
}

View file

@ -0,0 +1,2 @@
/** @since 3.1.0 */
export declare const onNuxtReady: (callback: () => any) => void;

View file

@ -0,0 +1,15 @@
import { useNuxtApp } from "../nuxt.js";
import { requestIdleCallback } from "../compat/idle-callback.js";
export const onNuxtReady = (callback) => {
if (import.meta.server) {
return;
}
const nuxtApp = useNuxtApp();
if (nuxtApp.isHydrating) {
nuxtApp.hooks.hookOnce("app:suspense:resolve", () => {
requestIdleCallback(() => callback());
});
} else {
requestIdleCallback(() => callback());
}
};

Some files were not shown because too many files have changed in this diff Show more