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

21
Frontend-Learner/node_modules/nuxt/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-present - Nuxt Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

117
Frontend-Learner/node_modules/nuxt/README.md generated vendored Normal file
View file

@ -0,0 +1,117 @@
[![Nuxt banner](https://github.com/nuxt/nuxt/blob/main/.github/assets/banner.svg)](https://nuxt.com)
# Nuxt
<p>
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/v/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a>
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/dm/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a>
<a href="https://github.com/nuxt/nuxt/blob/main/LICENSE"><img src="https://img.shields.io/github/license/nuxt/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="License"></a>
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt%20Docs-18181B?logo=nuxt" alt="Website"></a>
<a href="https://chat.nuxt.dev"><img src="https://img.shields.io/badge/Nuxt%20Discord-18181B?logo=discord" alt="Discord"></a>
<a href="https://securityscorecards.dev/"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
</p>
Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.
It provides a number of features that make it easy to build fast, SEO-friendly, and scalable web applications, including:
- Server-side rendering, static site generation, hybrid rendering and edge-side rendering
- Automatic routing with code-splitting and pre-fetching
- Data fetching and state management
- Search engine optimization and defining meta tags
- Auto imports of components, composables and utils
- TypeScript with zero configuration
- Go full-stack with our server/ directory
- Extensible with [200+ modules](https://nuxt.com/modules)
- Deployment to a variety of [hosting platforms](https://nuxt.com/deploy)
- ...[and much more](https://nuxt.com) 🚀
### Table of Contents
- 🚀 [Getting Started](#getting-started)
- 💻 [ Vue Development](#vue-development)
- 📖 [Documentation](#documentation)
- 🧩 [Modules](#modules)
- ❤️ [Contribute](#contribute)
- 🏠 [Local Development](#local-development)
- 🛟 [Professional Support](#professional-support)
- 🔗 [Follow Us](#follow-us)
- ⚖️ [License](#license)
---
## <a name="getting-started">🚀 Getting Started</a>
Use the following command to create a new starter project. This will create a starter project with all the necessary files and dependencies:
```bash
npm create nuxt@latest <my-project>
```
> [!TIP]
> Discover also [nuxt.new](https://nuxt.new): Open a Nuxt starter on CodeSandbox, StackBlitz or locally to get up and running in a few seconds.
## <a name="vue-development">💻 Vue Development</a>
Simple, intuitive and powerful, Nuxt lets you write Vue components in a way that makes sense. Every repetitive task is automated, so you can focus on writing your full-stack Vue application with confidence.
Example of an `app.vue`:
```vue
<script setup lang="ts">
useSeoMeta({
title: 'Meet Nuxt',
description: 'The Intuitive Vue Framework.',
})
</script>
<template>
<div id="app">
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
<style scoped>
#app {
background-color: #020420;
color: #00DC82;
}
</style>
```
## <a name="documentation">📖 Documentation</a>
We highly recommend you take a look at the [Nuxt documentation](https://nuxt.com/docs) to level up. Its a great resource for learning more about the framework. It covers everything from getting started to advanced topics.
## <a name="modules">🧩 Modules</a>
Discover our [list of modules](https://nuxt.com/modules) to supercharge your Nuxt project, created by the Nuxt team and community.
## <a name="contribute">❤️ Contribute</a>
We invite you to contribute and help improve Nuxt 💚
Here are a few ways you can get involved:
- **Reporting Bugs:** If you come across any bugs or issues, please check out the [reporting bugs guide](https://nuxt.com/docs/community/reporting-bugs) to learn how to submit a bug report.
- **Suggestions:** Have ideas to enhance Nuxt? We'd love to hear them! Check out the [contribution guide](https://nuxt.com/docs/community/contribution) to share your suggestions.
- **Questions:** If you have questions or need assistance, the [getting help guide](https://nuxt.com/docs/community/getting-help) provides resources to help you out.
## <a name="local-development">🏠 Local Development</a>
Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/docs/community/framework-contribution#setup) to contribute to the framework and documentation.
## <a name="professional-support">🛟 Professional Support</a>
- Technical audit & consulting: [Nuxt Experts](https://nuxt.com/enterprise/support)
- Custom development & more: [Nuxt Agencies Partners](https://nuxt.com/enterprise/agencies)
## <a name="follow-us">🔗 Follow Us</a>
<p valign="center">
<a href="https://go.nuxt.com/discord"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/github.svg" alt="GitHub"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/bluesky"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
</p>
## <a name="license">⚖️ License</a>
[MIT](https://github.com/nuxt/nuxt/blob/main/LICENSE)

1
Frontend-Learner/node_modules/nuxt/app.d.ts generated vendored Normal file
View file

@ -0,0 +1 @@
export * from './dist/app/index'

1
Frontend-Learner/node_modules/nuxt/app/defaults.d.ts generated vendored Normal file
View file

@ -0,0 +1 @@
export * from '../dist/app/defaults'

2
Frontend-Learner/node_modules/nuxt/bin/nuxt.mjs generated vendored Normal file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env node
import '@nuxt/cli/cli'

7
Frontend-Learner/node_modules/nuxt/config.cjs generated vendored Normal file
View file

@ -0,0 +1,7 @@
function defineNuxtConfig (config) {
return config
}
module.exports = {
defineNuxtConfig,
}

8
Frontend-Learner/node_modules/nuxt/config.d.ts generated vendored Normal file
View file

@ -0,0 +1,8 @@
import type { ConfigLayerMeta, DefineConfig } from 'c12'
import type { NuxtConfig } from 'nuxt/schema'
export { NuxtConfig } from 'nuxt/schema'
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface DefineNuxtConfig extends DefineConfig<NuxtConfig, ConfigLayerMeta> {}
export declare const defineNuxtConfig: DefineNuxtConfig

5
Frontend-Learner/node_modules/nuxt/config.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
function defineNuxtConfig (config) {
return config
}
export { defineNuxtConfig }

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;
}
});
}

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