@nuxtjs/seo 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ <h1 align='center'>Nuxt SEO</h1>
2
+
3
+ <p align="center">
4
+ <a href='https://github.com/harlan-zw/nuxt-seo/actions/workflows/test.yml'>
5
+ </a>
6
+ <a href="https://www.npmjs.com/package/@nuxtjs/seo" target="__blank"><img src="https://img.shields.io/npm/v/@nuxtjs/seo?style=flat&colorA=002438&colorB=28CF8D" alt="NPM version"></a>
7
+ <a href="https://www.npmjs.com/package/@nuxtjs/seo" target="__blank"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@nuxtjs/seo?flat&colorA=002438&colorB=28CF8D"></a>
8
+ <a href="https://github.com/harlan-zw/nuxt-seo" target="__blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/harlan-zw/nuxt-seo?flat&colorA=002438&colorB=28CF8D"></a>
9
+ </p>
10
+
11
+ <p align="center">
12
+ The complete SEO solution for Nuxt.
13
+ </p>
14
+
15
+ <p align="center">
16
+ <table>
17
+ <tbody>
18
+ <td align="center">
19
+ <img width="800" height="0" /><br>
20
+ <i>Status:</i> <a href="https://github.com/harlan-zw/nuxt-seo/releases/tag/v2.0.0">v2 Released</a></b> <br>
21
+ <sup> Please report any issues 🐛</sup><br>
22
+ <sub>Made possible by my <a href="https://github.com/sponsors/harlan-zw">Sponsor Program 💖</a><br> Follow me <a href="https://twitter.com/harlan_zw">@harlan_zw</a> 🐦 • Join <a href="https://discord.gg/275MBUBvgP">Discord</a> for help</sub><br>
23
+ <img width="800" height="0" />
24
+ </td>
25
+ </tbody>
26
+ </table>
27
+ </p>
28
+
29
+ ## Background
30
+
31
+ Technical SEO is hard. It requires many moving parts that need to work well together. Configuring all of these parts
32
+ correctly is a challenge.
33
+
34
+ Nuxt SEO is the total SEO solution for Nuxt. It combines 7 SEO modules and best practices into one module that requires
35
+ minimal effort to configure.
36
+
37
+ With powerful APIs built for fully dynamic sites and zero-config defaults for static sites.
38
+
39
+ ## Modules
40
+
41
+ - 📖 [nuxt-simple-sitemap](https://github.com/nuxt-modules/sitemap) - Sitemap.xml Support
42
+ - 🤖 [nuxt-simple-robots](https://github.com/harlan-zw/nuxt-simple-robots) - Manage site crawling
43
+ - 🔎 [nuxt-schema-org](https://unhead-schema-org.harlanzw.com/) - Generate Schema.org JSON-LD for SEO
44
+ - △ [nuxt-seo-experiments](https://github.com/harlan-zw/nuxt-seo-experiments) - Experimental SEO meta features
45
+ - 🖼️ [nuxt-og-image](https://github.com/nuxt-modules/og-image) - Generate dynamic social share images
46
+ - ✅ [nuxt-link-checker](https://github.com/harlan-zw/nuxt-link-checker) - Check for broken links
47
+
48
+ ## Install
49
+
50
+ 1. Install `@nuxtjs/seo` dependency to your project:
51
+
52
+ ```sh
53
+ pnpm i -D @nuxtjs/seo
54
+ yarn add -D @nuxtjs/seo
55
+ npm install -D @nuxtjs/seo
56
+ ```
57
+
58
+ 2. Add it to your `modules` section in your `nuxt.config`:
59
+
60
+ ```ts [nuxt.config]
61
+ export default defineNuxtConfig({
62
+ modules: ['@nuxtjs/seo']
63
+ })
64
+ ```
65
+
66
+ That's it!
67
+
68
+ All features are enabled by default. Learn more by exploring the [documentation](https://nuxtseo.com)
69
+
70
+ ## Sponsors
71
+
72
+ <p align="center">
73
+ <a href="https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg">
74
+ <img src='https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg'/>
75
+ </a>
76
+ </p>
77
+
78
+ ## License
79
+
80
+ MIT License © 2022-PRESENT [Harlan Wilton](https://github.com/harlan-zw)
@@ -0,0 +1,5 @@
1
+ module.exports = function(...args) {
2
+ return import('./module.mjs').then(m => m.default.call(this, ...args))
3
+ }
4
+ const _meta = module.exports.meta = require('./module.json')
5
+ module.exports.getMeta = () => Promise.resolve(_meta)
@@ -0,0 +1,46 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ /**
5
+ * Will ensure a title is always set by providing a fallback title based on the casing the last slug segment.
6
+ *
7
+ * @default true
8
+ */
9
+ fallbackTitle?: boolean;
10
+ /**
11
+ * Will set up a number of defaults for meta tags and Schema.org, if the modules and config are available.
12
+ *
13
+ * @default true
14
+ */
15
+ automaticDefaults?: boolean;
16
+ /**
17
+ * When enabled, it will redirect any request to the canonical domain (site url) using a 301 redirect on non-dev environments.
18
+ *
19
+ * E.g if the site url is 'www.example.com' and the user visits 'example.com',
20
+ * they will be redirected to 'www.example.com'.
21
+ *
22
+ * This is useful for SEO as it prevents duplicate content and consolidates page rank.
23
+ *
24
+ * @default false
25
+ */
26
+ redirectToCanonicalSiteUrl?: boolean;
27
+ /**
28
+ * Whether the module should be loaded.
29
+ */
30
+ enabled: boolean;
31
+ /**
32
+ * Whether the debugging mode should be enabled.
33
+ *
34
+ * @default `nuxt.options.debug`
35
+ */
36
+ debug: boolean;
37
+ /**
38
+ * Whether the Nuxt SEO splash should be shown when Nuxt is started.
39
+ *
40
+ * @default `nuxt.options.dev`
41
+ */
42
+ splash: boolean;
43
+ }
44
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
45
+
46
+ export { type ModuleOptions, _default as default };
@@ -0,0 +1,46 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ /**
5
+ * Will ensure a title is always set by providing a fallback title based on the casing the last slug segment.
6
+ *
7
+ * @default true
8
+ */
9
+ fallbackTitle?: boolean;
10
+ /**
11
+ * Will set up a number of defaults for meta tags and Schema.org, if the modules and config are available.
12
+ *
13
+ * @default true
14
+ */
15
+ automaticDefaults?: boolean;
16
+ /**
17
+ * When enabled, it will redirect any request to the canonical domain (site url) using a 301 redirect on non-dev environments.
18
+ *
19
+ * E.g if the site url is 'www.example.com' and the user visits 'example.com',
20
+ * they will be redirected to 'www.example.com'.
21
+ *
22
+ * This is useful for SEO as it prevents duplicate content and consolidates page rank.
23
+ *
24
+ * @default false
25
+ */
26
+ redirectToCanonicalSiteUrl?: boolean;
27
+ /**
28
+ * Whether the module should be loaded.
29
+ */
30
+ enabled: boolean;
31
+ /**
32
+ * Whether the debugging mode should be enabled.
33
+ *
34
+ * @default `nuxt.options.debug`
35
+ */
36
+ debug: boolean;
37
+ /**
38
+ * Whether the Nuxt SEO splash should be shown when Nuxt is started.
39
+ *
40
+ * @default `nuxt.options.dev`
41
+ */
42
+ splash: boolean;
43
+ }
44
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
45
+
46
+ export { type ModuleOptions, _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "nuxtseo",
3
+ "compatibility": {
4
+ "nuxt": "^3.7.0",
5
+ "bridge": false
6
+ },
7
+ "configKey": "seo",
8
+ "version": "2.0.0-rc.1"
9
+ }
@@ -0,0 +1,102 @@
1
+ import { defineNuxtModule, useLogger, createResolver, installModule, addPlugin, hasNuxtModule, addImports, addServerHandler } from '@nuxt/kit';
2
+ import chalk from 'chalk';
3
+ import { installNuxtSiteConfig } from 'nuxt-site-config-kit';
4
+
5
+ const version = "2.0.0-rc.1";
6
+
7
+ const Modules = [
8
+ "nuxt-simple-robots",
9
+ "nuxt-simple-sitemap",
10
+ "nuxt-og-image",
11
+ "nuxt-schema-org",
12
+ "nuxt-seo-experiments",
13
+ "nuxt-link-checker"
14
+ ];
15
+ const module = defineNuxtModule({
16
+ meta: {
17
+ name: "nuxtseo",
18
+ compatibility: {
19
+ nuxt: "^3.7.0",
20
+ bridge: false
21
+ },
22
+ configKey: "seo"
23
+ },
24
+ defaults(nuxt) {
25
+ return {
26
+ enabled: true,
27
+ debug: nuxt.options.debug,
28
+ redirectToCanonicalSiteUrl: false,
29
+ splash: nuxt.options.dev,
30
+ automaticDefaults: true,
31
+ fallbackTitle: true
32
+ };
33
+ },
34
+ async setup(config, nuxt) {
35
+ const logger = useLogger("@nuxtjs/seo");
36
+ logger.level = config.debug ? 4 : 3;
37
+ if (config.enabled === false) {
38
+ logger.debug("The module is disabled, skipping setup.");
39
+ return;
40
+ }
41
+ const { resolve, resolvePath } = createResolver(import.meta.url);
42
+ await installNuxtSiteConfig();
43
+ for (const module of Modules)
44
+ await installModule(await resolvePath(module));
45
+ if (config.automaticDefaults) {
46
+ addPlugin({
47
+ src: resolve("./runtime/nuxt/plugin/defaults")
48
+ });
49
+ }
50
+ if (config.fallbackTitle) {
51
+ addPlugin({
52
+ src: resolve("./runtime/nuxt/plugin/titles")
53
+ });
54
+ }
55
+ if (!hasNuxtModule("@nuxtjs/i18n")) {
56
+ addImports({
57
+ from: resolve(`./runtime/nuxt/composables/polyfills`),
58
+ name: "useI18n"
59
+ });
60
+ }
61
+ addImports({
62
+ from: resolve(`./runtime/nuxt/composables/useBreadcrumbItems`),
63
+ name: "useBreadcrumbItems"
64
+ });
65
+ const polyfills = {
66
+ schemaOrg: ["useSchemaOrg", "defineWebSite", "defineWebPage"]
67
+ };
68
+ for (const [module, composables] of Object.entries(polyfills)) {
69
+ if (nuxt.options[module]?.enable === false) {
70
+ composables.forEach((name) => {
71
+ addImports({
72
+ from: resolve("./runtime/nuxt/composables/polyfills"),
73
+ name
74
+ });
75
+ });
76
+ }
77
+ }
78
+ nuxt.options.experimental.headNext = true;
79
+ if (config.redirectToCanonicalSiteUrl) {
80
+ addServerHandler({
81
+ handler: resolve("./runtime/nitro/middleware/redirect"),
82
+ middleware: true
83
+ });
84
+ }
85
+ if (config.splash) {
86
+ logger.log("");
87
+ let latestTag = `v${version}`;
88
+ try {
89
+ latestTag = (await $fetch("https://ungh.unjs.io/repos/harlan-zw/nuxt-seo/releases/latest")).release.tag;
90
+ } catch (e) {
91
+ }
92
+ const upToDate = latestTag === `v${version}`;
93
+ logger.log(`${chalk.green("Nuxt SEO")} ${chalk.yellow(`v${version}`)} ${chalk.gray(`by ${chalk.underline("@harlan_zw")}`)}`);
94
+ if (!upToDate)
95
+ logger.log(`${chalk.gray(" \u251C\u2500 ")}\u{1F389} New version available!${chalk.gray(` Run ${chalk.underline(`npm i @nuxtjs/seo@${latestTag}`)} to update.`)}`);
96
+ logger.log(chalk.dim(" \u2514\u2500 \u{1F9EA} Help get Nuxt SEO stable by providing feedback https://github.com/harlan-zw/nuxt-seo/discussions/108"));
97
+ logger.log("");
98
+ }
99
+ }
100
+ });
101
+
102
+ export { module as default };
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void> | undefined>;
2
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { defineEventHandler, sendRedirect } from "h3";
2
+ import { joinURL } from "ufo";
3
+ import { useNitroOrigin, useSiteConfig } from "#imports";
4
+ export default defineEventHandler((e) => {
5
+ const siteConfig = useSiteConfig(e);
6
+ if (siteConfig.site) {
7
+ const origin = useNitroOrigin(e);
8
+ if (!siteConfig.site.startsWith(origin)) {
9
+ const url = new URL(e.path, origin);
10
+ url.hostname = siteConfig.site;
11
+ return sendRedirect(e, joinURL(siteConfig.site, url.pathname), 301);
12
+ }
13
+ }
14
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../.nuxt/tsconfig.server.json"
3
+ }
@@ -0,0 +1,10 @@
1
+ export declare function useSchemaOrg(): void;
2
+ export declare function defineWebSite(): void;
3
+ export declare function defineWebPage(): void;
4
+ export declare function useI18n(): {
5
+ t: (_: string, fallback: string) => string;
6
+ te: (_: string) => boolean;
7
+ strategy: string;
8
+ defaultLocale: import("vue").Ref<any>;
9
+ locale: import("vue").Ref<any>;
10
+ };
@@ -0,0 +1,18 @@
1
+ import { ref } from "vue";
2
+ import { useSiteConfig } from "#imports";
3
+ export function useSchemaOrg() {
4
+ }
5
+ export function defineWebSite() {
6
+ }
7
+ export function defineWebPage() {
8
+ }
9
+ export function useI18n() {
10
+ const siteConfig = useSiteConfig();
11
+ return {
12
+ t: (_, fallback) => fallback,
13
+ te: (_) => false,
14
+ strategy: "no_prefix",
15
+ defaultLocale: ref(siteConfig.defaultLocale || "en"),
16
+ locale: ref(siteConfig.currentLocale || siteConfig.defaultLocale || "en")
17
+ };
18
+ }
@@ -0,0 +1,48 @@
1
+ import type { MaybeRefOrGetter } from 'vue';
2
+ import type { BreadcrumbLink } from '@nuxt/ui/dist/runtime/types';
3
+ export interface BreadcrumbProps {
4
+ path?: MaybeRefOrGetter<string>;
5
+ id?: string;
6
+ schemaOrg?: boolean;
7
+ /**
8
+ * The Aria Label for the breadcrumbs.
9
+ * You shouldn't need to change this.
10
+ *
11
+ * @default 'Breadcrumbs'
12
+ */
13
+ ariaLabel?: string;
14
+ /**
15
+ * Should the current breadcrumb item be shown.
16
+ *
17
+ * @default false
18
+ */
19
+ hideCurrent?: MaybeRefOrGetter<boolean>;
20
+ /**
21
+ * Should the root breadcrumb be shown.
22
+ */
23
+ hideRoot?: MaybeRefOrGetter<boolean>;
24
+ }
25
+ export interface BreadcrumbItemProps extends BreadcrumbLink {
26
+ /** Whether the breadcrumb item represents the aria-current. */
27
+ current?: boolean;
28
+ /**
29
+ * The type of current location the breadcrumb item represents, if `isCurrent` is true.
30
+ * @default 'page'
31
+ */
32
+ ariaCurrent?: 'page' | 'step' | 'location' | 'date' | 'time' | boolean | 'true' | 'false';
33
+ /** Whether the breadcrumb item is disabled. */
34
+ disabled?: boolean;
35
+ to: string;
36
+ ariaLabel?: string;
37
+ separator?: boolean | string;
38
+ icon?: string;
39
+ class?: (string | string[] | undefined)[] | string;
40
+ /**
41
+ * @internal
42
+ */
43
+ _props?: {
44
+ first: boolean;
45
+ last: boolean;
46
+ };
47
+ }
48
+ export declare function useBreadcrumbItems(options?: BreadcrumbProps): any;
@@ -0,0 +1,78 @@
1
+ import { withoutTrailingSlash } from "ufo";
2
+ import { pathBreadcrumbSegments } from "../../pure/breadcrumbs.mjs";
3
+ import {
4
+ computed,
5
+ defineBreadcrumb,
6
+ toValue,
7
+ useI18n,
8
+ useRoute,
9
+ useRouter,
10
+ useSchemaOrg,
11
+ withSiteTrailingSlash
12
+ } from "#imports";
13
+ function withoutQuery(path) {
14
+ return path.split("?")[0];
15
+ }
16
+ function titleCase(s) {
17
+ return s.replaceAll("-", " ").replace(/\w\S*/g, (w) => w.charAt(0).toUpperCase() + w.substr(1).toLowerCase());
18
+ }
19
+ export function useBreadcrumbItems(options = {}) {
20
+ const router = useRouter();
21
+ const routes = router.getRoutes();
22
+ const i18n = useI18n();
23
+ const items = computed(() => {
24
+ let rootNode = "/";
25
+ if (i18n) {
26
+ if (i18n.strategy === "prefix" || i18n.strategy !== "no_prefix" && i18n.defaultLocale.value !== i18n.locale.value)
27
+ rootNode = `/${i18n.defaultLocale.value}`;
28
+ }
29
+ const current = withoutQuery(withoutTrailingSlash(toValue(options.path || useRoute().path) || rootNode));
30
+ return pathBreadcrumbSegments(current, rootNode).map((path) => ({
31
+ to: path
32
+ })).map((item) => {
33
+ const route = routes.find((r) => withoutTrailingSlash(r.path) === withoutTrailingSlash(item.to));
34
+ const routeMeta = route?.meta || {};
35
+ const routeName = route ? String(route.name || route.path) : item.to === "/" ? "index" : "unknown";
36
+ let [name] = routeName.split("___");
37
+ if (name === "unknown")
38
+ name = item.to.split("/").pop() || "";
39
+ if (routeMeta.breadcrumb) {
40
+ item = {
41
+ ...item,
42
+ ...routeMeta.breadcrumb
43
+ };
44
+ }
45
+ item.label = item.label || routeMeta.breadcrumbTitle || routeMeta.title;
46
+ if (typeof item.label === "undefined") {
47
+ item.label = item.label || i18n.t(`breadcrumb.items.${name}.label`, name === "index" ? "Home" : titleCase(name), { missingWarn: false });
48
+ item.ariaLabel = item.ariaLabel || i18n.t(`breadcrumb.items.${name}.ariaLabel`, item.label, { missingWarn: false });
49
+ }
50
+ item.ariaLabel = item.ariaLabel || item.label;
51
+ item.current = item.current || item.to === current;
52
+ if (toValue(options.hideCurrent) && item.current)
53
+ return false;
54
+ return item;
55
+ }).map((m) => {
56
+ if (m && m.to) {
57
+ m.to = withSiteTrailingSlash(m.to).value;
58
+ if (m.to === rootNode && toValue(options.hideRoot))
59
+ return false;
60
+ }
61
+ return m;
62
+ }).filter(Boolean);
63
+ });
64
+ if (process.server && options.schemaOrg) {
65
+ useSchemaOrg([
66
+ defineBreadcrumb(computed(() => {
67
+ return {
68
+ id: `#${options.id || "breadcrumb"}`,
69
+ itemListElement: items.value.map((item) => ({
70
+ name: item.label || item.ariaLabel,
71
+ item: item.to
72
+ }))
73
+ };
74
+ }))
75
+ ]);
76
+ }
77
+ return items;
78
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,49 @@
1
+ import {
2
+ computed,
3
+ createSitePathResolver,
4
+ defineNuxtPlugin,
5
+ useHead,
6
+ useRoute,
7
+ useSeoMeta,
8
+ useServerHead,
9
+ useSiteConfig
10
+ } from "#imports";
11
+ export default defineNuxtPlugin({
12
+ name: "nuxt-seo:defaults",
13
+ setup() {
14
+ const siteConfig = useSiteConfig() || {};
15
+ const route = useRoute();
16
+ const resolveUrl = createSitePathResolver({ withBase: true, absolute: true });
17
+ const canonicalUrl = computed(() => resolveUrl(route.path || "/").value || route.path);
18
+ const minimalPriority = {
19
+ // give nuxt.config values higher priority
20
+ tagPriority: 101
21
+ };
22
+ useHead({
23
+ link: [{ rel: "canonical", href: () => canonicalUrl.value }]
24
+ });
25
+ const locale = siteConfig.currentLocale || siteConfig.defaultLocale;
26
+ if (locale) {
27
+ useServerHead({
28
+ htmlAttrs: { lang: locale }
29
+ });
30
+ }
31
+ useHead({
32
+ templateParams: { site: siteConfig, siteName: siteConfig.name || "" },
33
+ titleTemplate: "%s %separator %siteName"
34
+ }, minimalPriority);
35
+ const seoMeta = {
36
+ ogUrl: () => canonicalUrl.value,
37
+ ogLocale: locale,
38
+ ogSiteName: siteConfig.name
39
+ };
40
+ if (siteConfig.description)
41
+ seoMeta.description = siteConfig.description;
42
+ if (siteConfig.twitter) {
43
+ const id = siteConfig.twitter.startsWith("@") ? siteConfig.twitter : `@${siteConfig.twitter}`;
44
+ seoMeta.twitterCreator = id;
45
+ seoMeta.twitterSite = id;
46
+ }
47
+ useSeoMeta(seoMeta, minimalPriority);
48
+ }
49
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,28 @@
1
+ import { withoutTrailingSlash } from "ufo";
2
+ import {
3
+ computed,
4
+ defineNuxtPlugin,
5
+ useHead,
6
+ useRoute
7
+ } from "#imports";
8
+ function titleCase(s) {
9
+ return s.replaceAll("-", " ").replace(/\w\S*/g, (w) => w.charAt(0).toUpperCase() + w.substr(1).toLowerCase());
10
+ }
11
+ export default defineNuxtPlugin({
12
+ name: "nuxt-seo:fallback-titles",
13
+ setup() {
14
+ const route = useRoute();
15
+ const title = computed(() => {
16
+ if (typeof route.meta?.title === "string")
17
+ return route.meta?.title;
18
+ const path = withoutTrailingSlash(route.path || "/");
19
+ const lastSegment = path.split("/").pop();
20
+ return lastSegment ? titleCase(lastSegment) : null;
21
+ });
22
+ const minimalPriority = {
23
+ // give nuxt.config values higher priority
24
+ tagPriority: 101
25
+ };
26
+ useHead({ title: () => title.value }, minimalPriority);
27
+ }
28
+ });
@@ -0,0 +1 @@
1
+ export declare function pathBreadcrumbSegments(path: string, rootNode?: string): string[];
@@ -0,0 +1,18 @@
1
+ import { hasTrailingSlash, parseURL, stringifyParsedURL, withTrailingSlash } from "ufo";
2
+ export function pathBreadcrumbSegments(path, rootNode = "/") {
3
+ const startNode = parseURL(path);
4
+ const appendsTrailingSlash = hasTrailingSlash(startNode.pathname);
5
+ const stepNode = (node, nodes = []) => {
6
+ const fullPath = stringifyParsedURL(node);
7
+ const currentPathName = node.pathname || "/";
8
+ nodes.push(fullPath || "/");
9
+ if (currentPathName !== rootNode && currentPathName !== "/") {
10
+ node.pathname = currentPathName.substring(0, currentPathName.lastIndexOf("/"));
11
+ if (appendsTrailingSlash)
12
+ node.pathname = withTrailingSlash(node.pathname.substring(0, node.pathname.lastIndexOf("/")));
13
+ stepNode(node, nodes);
14
+ }
15
+ return nodes;
16
+ };
17
+ return stepNode(startNode).reverse();
18
+ }
@@ -0,0 +1,16 @@
1
+
2
+ import type { ModuleOptions } from './module.js'
3
+
4
+
5
+ declare module '@nuxt/schema' {
6
+ interface NuxtConfig { ['seo']?: Partial<ModuleOptions> }
7
+ interface NuxtOptions { ['seo']?: ModuleOptions }
8
+ }
9
+
10
+ declare module 'nuxt/schema' {
11
+ interface NuxtConfig { ['seo']?: Partial<ModuleOptions> }
12
+ interface NuxtOptions { ['seo']?: ModuleOptions }
13
+ }
14
+
15
+
16
+ export type { ModuleOptions, default } from './module.js'
@@ -0,0 +1,16 @@
1
+
2
+ import type { ModuleOptions } from './module'
3
+
4
+
5
+ declare module '@nuxt/schema' {
6
+ interface NuxtConfig { ['seo']?: Partial<ModuleOptions> }
7
+ interface NuxtOptions { ['seo']?: ModuleOptions }
8
+ }
9
+
10
+ declare module 'nuxt/schema' {
11
+ interface NuxtConfig { ['seo']?: Partial<ModuleOptions> }
12
+ interface NuxtOptions { ['seo']?: ModuleOptions }
13
+ }
14
+
15
+
16
+ export type { ModuleOptions, default } from './module'
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@nuxtjs/seo",
3
+ "type": "module",
4
+ "version": "2.0.0-rc.1",
5
+ "packageManager": "pnpm@8.14.0",
6
+ "description": "The all-in-one SEO layer for Nuxt 3.",
7
+ "author": {
8
+ "name": "Harlan Wilton",
9
+ "email": "harlan@harlanzw.com",
10
+ "url": "https://harlanzw.com/"
11
+ },
12
+ "license": "MIT",
13
+ "funding": "https://github.com/sponsors/harlan-zw",
14
+ "homepage": "https://nuxtseo.com/",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/harlan-zw/nuxt-seo.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/harlan-zw/nuxt-seo/issues"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "dependencies": {
26
+ "@nuxt/kit": "^3.9.0",
27
+ "chalk": "^5.3.0",
28
+ "defu": "^6.1.3",
29
+ "nuxt-link-checker": "^3.0.0-rc.3",
30
+ "nuxt-og-image": "^3.0.0-rc.21",
31
+ "nuxt-schema-org": "^3.3.0",
32
+ "nuxt-seo-experiments": "^4.0.0-rc.0",
33
+ "nuxt-simple-robots": "^4.0.0-rc.9",
34
+ "nuxt-simple-sitemap": "^4.4.1",
35
+ "nuxt-site-config": "^2.2.0",
36
+ "nuxt-site-config-kit": "^2.2.0",
37
+ "ufo": "^1.3.2"
38
+ },
39
+ "devDependencies": {
40
+ "@antfu/eslint-config": "^2.6.1",
41
+ "@nuxt/module-builder": "^0.5.5",
42
+ "@nuxt/schema": "^3.9.0",
43
+ "@nuxt/test-utils": "3.9.0",
44
+ "@nuxt/ui": "^2.11.1",
45
+ "@nuxtjs/i18n": "8.0.0",
46
+ "bumpp": "^9.2.1",
47
+ "eslint": "^8.56.0",
48
+ "execa": "^8.0.1",
49
+ "nitropack": "^2.8.1",
50
+ "nuxt": "^3.9.0",
51
+ "typescript": "^5.3.3",
52
+ "vitest": "^1.1.1"
53
+ },
54
+ "scripts": {
55
+ "build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build",
56
+ "dev": "nuxi dev .playground",
57
+ "lint": "eslint . --fix",
58
+ "release": "bumpp && pnpm -r publish --access public",
59
+ "test": "nuxi prepare .playground && true",
60
+ "typecheck": "tsc --noEmit --strict"
61
+ }
62
+ }