@nuxtjs/seo 2.0.0-rc.1 → 2.0.0-rc.11

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 CHANGED
@@ -17,7 +17,7 @@ The complete SEO solution for Nuxt.
17
17
  <tbody>
18
18
  <td align="center">
19
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>
20
+ <i>Status:</i> <a href="https://github.com/harlan-zw/nuxt-seo/releases/tag/v2.0.0">v2 RC is available</a></b> <br>
21
21
  <sup> Please report any issues 🐛</sup><br>
22
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
23
  <img width="800" height="0" />
@@ -38,9 +38,9 @@ With powerful APIs built for fully dynamic sites and zero-config defaults for st
38
38
 
39
39
  ## Modules
40
40
 
41
- - 📖 [nuxt-simple-sitemap](https://github.com/nuxt-modules/sitemap) - Sitemap.xml Support
41
+ - 📖 [@nuxtjs/sitemap](https://github.com/nuxt-modules/sitemap) - Sitemap.xml Support
42
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
43
+ - 🔎 [nuxt-schema-org](https://github.com/harlan-zw/nuxt-schema-org) - Generate Schema.org JSON-LD for SEO
44
44
  - △ [nuxt-seo-experiments](https://github.com/harlan-zw/nuxt-seo-experiments) - Experimental SEO meta features
45
45
  - 🖼️ [nuxt-og-image](https://github.com/nuxt-modules/og-image) - Generate dynamic social share images
46
46
  - ✅ [nuxt-link-checker](https://github.com/harlan-zw/nuxt-link-checker) - Check for broken links
@@ -50,23 +50,18 @@ With powerful APIs built for fully dynamic sites and zero-config defaults for st
50
50
  1. Install `@nuxtjs/seo` dependency to your project:
51
51
 
52
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
- })
53
+ npx nuxi@latest module add seo
64
54
  ```
65
55
 
66
56
  That's it!
67
57
 
68
58
  All features are enabled by default. Learn more by exploring the [documentation](https://nuxtseo.com)
69
59
 
60
+ ## Stackblitz
61
+
62
+ For reproductions and demo environments, you can use the [Nuxt SEO Basic Reproduction](https://stackblitz.com/edit/nuxt-starter-gfrej6?file=nuxt.config.ts)
63
+ template.
64
+
70
65
  ## Sponsors
71
66
 
72
67
  <p align="center">
package/dist/module.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "nuxtseo",
3
3
  "compatibility": {
4
- "nuxt": "^3.7.0",
4
+ "nuxt": ">=3.7.0",
5
5
  "bridge": false
6
6
  },
7
7
  "configKey": "seo",
8
- "version": "2.0.0-rc.1"
8
+ "version": "2.0.0-rc.10",
9
+ "builder": {
10
+ "@nuxt/module-builder": "0.8.0",
11
+ "unbuild": "2.0.0"
12
+ }
9
13
  }
package/dist/module.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { defineNuxtModule, useLogger, createResolver, installModule, addPlugin, hasNuxtModule, addImports, addServerHandler } from '@nuxt/kit';
2
- import chalk from 'chalk';
1
+ import { defineNuxtModule, createResolver, useLogger, installModule, hasNuxtModule, addPlugin, addImports, addServerHandler } from '@nuxt/kit';
2
+ import { colors } from 'consola/utils';
3
3
  import { installNuxtSiteConfig } from 'nuxt-site-config-kit';
4
-
5
- const version = "2.0.0-rc.1";
4
+ import { readPackageJSON } from 'pkg-types';
5
+ import { $fetch } from 'ofetch';
6
6
 
7
7
  const Modules = [
8
8
  "nuxt-simple-robots",
9
- "nuxt-simple-sitemap",
9
+ "@nuxtjs/sitemap",
10
10
  "nuxt-og-image",
11
11
  "nuxt-schema-org",
12
12
  "nuxt-seo-experiments",
@@ -16,7 +16,7 @@ const module = defineNuxtModule({
16
16
  meta: {
17
17
  name: "nuxtseo",
18
18
  compatibility: {
19
- nuxt: "^3.7.0",
19
+ nuxt: ">=3.7.0",
20
20
  bridge: false
21
21
  },
22
22
  configKey: "seo"
@@ -26,26 +26,39 @@ const module = defineNuxtModule({
26
26
  enabled: true,
27
27
  debug: nuxt.options.debug,
28
28
  redirectToCanonicalSiteUrl: false,
29
- splash: nuxt.options.dev,
29
+ splash: false,
30
+ // nuxt.options.dev, - figure out a solution for this in the future
30
31
  automaticDefaults: true,
31
32
  fallbackTitle: true
32
33
  };
33
34
  },
34
35
  async setup(config, nuxt) {
35
- const logger = useLogger("@nuxtjs/seo");
36
+ const { resolve, resolvePath } = createResolver(import.meta.url);
37
+ const { name, version } = await readPackageJSON(resolve("../package.json"));
38
+ const logger = useLogger(name);
36
39
  logger.level = config.debug ? 4 : 3;
37
40
  if (config.enabled === false) {
38
41
  logger.debug("The module is disabled, skipping setup.");
39
42
  return;
40
43
  }
41
- const { resolve, resolvePath } = createResolver(import.meta.url);
42
44
  await installNuxtSiteConfig();
43
45
  for (const module of Modules)
44
46
  await installModule(await resolvePath(module));
45
47
  if (config.automaticDefaults) {
46
- addPlugin({
47
- src: resolve("./runtime/nuxt/plugin/defaults")
48
- });
48
+ if (hasNuxtModule("@nuxtjs/i18n")) {
49
+ addPlugin({
50
+ src: resolve(`./runtime/nuxt/plugin/defaultsWaitI18n.server`),
51
+ mode: "server"
52
+ });
53
+ addPlugin({
54
+ src: resolve(`./runtime/nuxt/plugin/defaults`),
55
+ mode: "client"
56
+ });
57
+ } else {
58
+ addPlugin({
59
+ src: resolve(`./runtime/nuxt/plugin/defaults`)
60
+ });
61
+ }
49
62
  }
50
63
  if (config.fallbackTitle) {
51
64
  addPlugin({
@@ -67,10 +80,10 @@ const module = defineNuxtModule({
67
80
  };
68
81
  for (const [module, composables] of Object.entries(polyfills)) {
69
82
  if (nuxt.options[module]?.enable === false) {
70
- composables.forEach((name) => {
83
+ composables.forEach((name2) => {
71
84
  addImports({
72
85
  from: resolve("./runtime/nuxt/composables/polyfills"),
73
- name
86
+ name: name2
74
87
  });
75
88
  });
76
89
  }
@@ -82,18 +95,19 @@ const module = defineNuxtModule({
82
95
  middleware: true
83
96
  });
84
97
  }
85
- if (config.splash) {
98
+ if (config.splash && !version.includes("rc") && nuxt.options.dev) {
86
99
  logger.log("");
87
100
  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
- }
101
+ latestTag = (await $fetch("https://ungh.unjs.io/repos/harlan-zw/nuxt-seo/releases/latest", {
102
+ timeout: 2e3
103
+ }).catch(() => {
104
+ return { release: { tag: `v${version}` } };
105
+ })).release.tag;
92
106
  const upToDate = latestTag === `v${version}`;
93
- logger.log(`${chalk.green("Nuxt SEO")} ${chalk.yellow(`v${version}`)} ${chalk.gray(`by ${chalk.underline("@harlan_zw")}`)}`);
107
+ logger.log(`${colors.green("Nuxt SEO")} ${colors.yellow(`v${version}`)} ${colors.gray(`by ${colors.underline("@harlan_zw")}`)}`);
94
108
  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"));
109
+ logger.log(`${colors.gray(" \u251C\u2500 ")}\u{1F389} New version available!${colors.gray(` Run ${colors.underline(`npm i @nuxtjs/seo@${latestTag}`)} to update.`)}`);
110
+ logger.log(colors.dim(" \u2514\u2500 \u{1F9EA} Help get Nuxt SEO stable by providing feedback https://github.com/harlan-zw/nuxt-seo/discussions/108"));
97
111
  logger.log("");
98
112
  }
99
113
  }
@@ -0,0 +1,13 @@
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.url) {
7
+ const siteConfigHostName = new URL(e.path, siteConfig.url).hostname;
8
+ const origin = useNitroOrigin(e);
9
+ const originHostname = new URL(e.path, origin).hostname;
10
+ if (originHostname !== siteConfigHostName)
11
+ return sendRedirect(e, joinURL(siteConfig.url, e.path), 301);
12
+ }
13
+ });
@@ -1,8 +1,48 @@
1
1
  import type { MaybeRefOrGetter } from 'vue';
2
- import type { BreadcrumbLink } from '@nuxt/ui/dist/runtime/types';
2
+ import type { NuxtLinkProps } from 'nuxt/app';
3
+ interface NuxtUIBreadcrumbItem extends NuxtLinkProps {
4
+ label: string;
5
+ labelClass?: string;
6
+ icon?: string;
7
+ iconClass?: string;
8
+ as?: string;
9
+ type?: string;
10
+ disabled?: boolean;
11
+ active?: boolean;
12
+ exact?: boolean;
13
+ exactQuery?: boolean;
14
+ exactMatch?: boolean;
15
+ inactiveClass?: string;
16
+ [key: string]: any;
17
+ }
3
18
  export interface BreadcrumbProps {
19
+ /**
20
+ * Generate the breadcrumbs based on a different path than the current route.
21
+ */
4
22
  path?: MaybeRefOrGetter<string>;
23
+ /**
24
+ * The id of the breadcrumb list. It's recommended to provide a unique
25
+ * id when adding multiple breadcrumb lists to the same page.
26
+ */
5
27
  id?: string;
28
+ /**
29
+ * Append additional breadcrumb items to the end of the list. This is applied
30
+ * after the `overrides` option.
31
+ */
32
+ append?: BreadcrumbItemProps[];
33
+ /**
34
+ * Prepend additional breadcrumb items to the start of the list. This is applied
35
+ * after the `overrides` option.
36
+ */
37
+ prepend?: BreadcrumbItemProps[];
38
+ /**
39
+ * Override any of the breadcrumb items based on the index.
40
+ */
41
+ overrides?: (BreadcrumbItemProps | false | undefined)[];
42
+ /**
43
+ * Should the schema.org breadcrumb be generated.
44
+ * @default true
45
+ */
6
46
  schemaOrg?: boolean;
7
47
  /**
8
48
  * The Aria Label for the breadcrumbs.
@@ -22,7 +62,7 @@ export interface BreadcrumbProps {
22
62
  */
23
63
  hideRoot?: MaybeRefOrGetter<boolean>;
24
64
  }
25
- export interface BreadcrumbItemProps extends BreadcrumbLink {
65
+ export interface BreadcrumbItemProps extends NuxtUIBreadcrumbItem {
26
66
  /** Whether the breadcrumb item represents the aria-current. */
27
67
  current?: boolean;
28
68
  /**
@@ -30,12 +70,9 @@ export interface BreadcrumbItemProps extends BreadcrumbLink {
30
70
  * @default 'page'
31
71
  */
32
72
  ariaCurrent?: 'page' | 'step' | 'location' | 'date' | 'time' | boolean | 'true' | 'false';
33
- /** Whether the breadcrumb item is disabled. */
34
- disabled?: boolean;
35
- to: string;
73
+ to?: string;
36
74
  ariaLabel?: string;
37
75
  separator?: boolean | string;
38
- icon?: string;
39
76
  class?: (string | string[] | undefined)[] | string;
40
77
  /**
41
78
  * @internal
@@ -45,4 +82,5 @@ export interface BreadcrumbItemProps extends BreadcrumbLink {
45
82
  last: boolean;
46
83
  };
47
84
  }
48
- export declare function useBreadcrumbItems(options?: BreadcrumbProps): any;
85
+ export declare function useBreadcrumbItems(options?: BreadcrumbProps): import("vue").ComputedRef<BreadcrumbItemProps[]>;
86
+ export {};
@@ -1,7 +1,9 @@
1
1
  import { withoutTrailingSlash } from "ufo";
2
- import { pathBreadcrumbSegments } from "../../pure/breadcrumbs.mjs";
2
+ import { defu } from "defu";
3
+ import { pathBreadcrumbSegments } from "../../pure/breadcrumbs.js";
3
4
  import {
4
5
  computed,
6
+ createSitePathResolver,
5
7
  defineBreadcrumb,
6
8
  toValue,
7
9
  useI18n,
@@ -20,22 +22,40 @@ export function useBreadcrumbItems(options = {}) {
20
22
  const router = useRouter();
21
23
  const routes = router.getRoutes();
22
24
  const i18n = useI18n();
25
+ const siteResolver = createSitePathResolver({
26
+ canonical: true,
27
+ absolute: true
28
+ });
23
29
  const items = computed(() => {
24
30
  let rootNode = "/";
25
31
  if (i18n) {
26
- if (i18n.strategy === "prefix" || i18n.strategy !== "no_prefix" && i18n.defaultLocale.value !== i18n.locale.value)
27
- rootNode = `/${i18n.defaultLocale.value}`;
32
+ if (i18n.strategy === "prefix" || i18n.strategy !== "no_prefix" && toValue(i18n.defaultLocale) !== toValue(i18n.locale))
33
+ rootNode = `/${toValue(i18n.locale)}`;
28
34
  }
29
35
  const current = withoutQuery(withoutTrailingSlash(toValue(options.path || useRoute().path) || rootNode));
30
- return pathBreadcrumbSegments(current, rootNode).map((path) => ({
31
- to: path
32
- })).map((item) => {
36
+ const overrides = options.overrides || [];
37
+ const segments = pathBreadcrumbSegments(current, rootNode).map((path, index) => {
38
+ let item = {
39
+ to: path
40
+ };
41
+ if (typeof overrides[index] !== "undefined") {
42
+ if (overrides[index] === false)
43
+ return false;
44
+ item = defu(overrides[index], item);
45
+ }
46
+ return item;
47
+ });
48
+ if (options.prepend)
49
+ segments.unshift(...options.prepend);
50
+ if (options.append)
51
+ segments.push(...options.append);
52
+ return segments.filter(Boolean).map((item) => {
33
53
  const route = routes.find((r) => withoutTrailingSlash(r.path) === withoutTrailingSlash(item.to));
34
54
  const routeMeta = route?.meta || {};
35
55
  const routeName = route ? String(route.name || route.path) : item.to === "/" ? "index" : "unknown";
36
56
  let [name] = routeName.split("___");
37
57
  if (name === "unknown")
38
- name = item.to.split("/").pop() || "";
58
+ name = (item.to || "").split("/").pop() || "";
39
59
  if (routeMeta.breadcrumb) {
40
60
  item = {
41
61
  ...item,
@@ -61,14 +81,15 @@ export function useBreadcrumbItems(options = {}) {
61
81
  return m;
62
82
  }).filter(Boolean);
63
83
  });
64
- if (process.server && options.schemaOrg) {
84
+ const schemaOrgEnabled = typeof options.schemaOrg === "undefined" ? true : options.schemaOrg;
85
+ if (import.meta.server && schemaOrgEnabled) {
65
86
  useSchemaOrg([
66
87
  defineBreadcrumb(computed(() => {
67
88
  return {
68
89
  id: `#${options.id || "breadcrumb"}`,
69
90
  itemListElement: items.value.map((item) => ({
70
91
  name: item.label || item.ariaLabel,
71
- item: item.to
92
+ item: item.to ? siteResolver(item.to) : void 0
72
93
  }))
73
94
  };
74
95
  }))
@@ -0,0 +1 @@
1
+ export declare function applyDefaults(): void;
@@ -0,0 +1,46 @@
1
+ import {
2
+ computed,
3
+ createSitePathResolver,
4
+ useHead,
5
+ useRoute,
6
+ useSeoMeta,
7
+ useServerHead,
8
+ useSiteConfig
9
+ } from "#imports";
10
+ export function applyDefaults() {
11
+ const siteConfig = useSiteConfig();
12
+ const route = useRoute();
13
+ const resolveUrl = createSitePathResolver({ withBase: true, absolute: true });
14
+ const canonicalUrl = computed(() => resolveUrl(route.path || "/").value || route.path);
15
+ const minimalPriority = {
16
+ // give nuxt.config values higher priority
17
+ tagPriority: 101
18
+ };
19
+ useHead({
20
+ link: [{ rel: "canonical", href: () => canonicalUrl.value }]
21
+ });
22
+ const locale = siteConfig.currentLocale || siteConfig.defaultLocale;
23
+ if (locale) {
24
+ useServerHead({
25
+ htmlAttrs: { lang: locale }
26
+ });
27
+ }
28
+ useHead({
29
+ templateParams: { site: siteConfig, siteName: siteConfig.name || "" },
30
+ titleTemplate: "%s %separator %siteName"
31
+ }, minimalPriority);
32
+ const seoMeta = {
33
+ ogType: "website",
34
+ ogUrl: () => canonicalUrl.value,
35
+ ogLocale: locale,
36
+ ogSiteName: siteConfig.name
37
+ };
38
+ if (siteConfig.description)
39
+ seoMeta.description = siteConfig.description;
40
+ if (siteConfig.twitter) {
41
+ const id = siteConfig.twitter.startsWith("@") ? siteConfig.twitter : `@${siteConfig.twitter}`;
42
+ seoMeta.twitterCreator = id;
43
+ seoMeta.twitterSite = id;
44
+ }
45
+ useSeoMeta(seoMeta, minimalPriority);
46
+ }
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
2
  export default _default;
@@ -0,0 +1,11 @@
1
+ import { applyDefaults as setup } from "../logic/applyDefaults.js";
2
+ import {
3
+ defineNuxtPlugin
4
+ } from "#imports";
5
+ export default defineNuxtPlugin({
6
+ name: "nuxt-seo:defaults",
7
+ env: {
8
+ islands: false
9
+ },
10
+ setup
11
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { applyDefaults as setup } from "../logic/applyDefaults.js";
2
+ import { defineNuxtPlugin } from "#imports";
3
+ export default defineNuxtPlugin({
4
+ name: "nuxt-seo:defaults",
5
+ env: {
6
+ islands: false
7
+ },
8
+ // we need to wait for the i18n plugin to run first
9
+ dependsOn: [
10
+ // @ts-expect-error dynamic
11
+ "nuxt-site-config:i18n"
12
+ ],
13
+ setup
14
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
2
  export default _default;
@@ -10,6 +10,9 @@ function titleCase(s) {
10
10
  }
11
11
  export default defineNuxtPlugin({
12
12
  name: "nuxt-seo:fallback-titles",
13
+ env: {
14
+ islands: false
15
+ },
13
16
  setup() {
14
17
  const route = useRoute();
15
18
  const title = computed(() => {
package/dist/types.d.mts CHANGED
@@ -1,16 +1 @@
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'
1
+ export { type ModuleOptions, default } from './module.js'
package/dist/types.d.ts CHANGED
@@ -1,16 +1 @@
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'
1
+ export { type ModuleOptions, default } from './module'
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/seo",
3
3
  "type": "module",
4
- "version": "2.0.0-rc.1",
5
- "packageManager": "pnpm@8.14.0",
4
+ "version": "2.0.0-rc.11",
6
5
  "description": "The all-in-one SEO layer for Nuxt 3.",
7
6
  "author": {
8
7
  "name": "Harlan Wilton",
@@ -19,44 +18,62 @@
19
18
  "bugs": {
20
19
  "url": "https://github.com/harlan-zw/nuxt-seo/issues"
21
20
  },
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/types.d.ts",
24
+ "import": "./dist/module.mjs",
25
+ "require": "./dist/module.cjs"
26
+ }
27
+ },
28
+ "main": "./dist/module.cjs",
29
+ "types": "./dist/types.d.ts",
22
30
  "files": [
23
31
  "dist"
24
32
  ],
25
33
  "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"
34
+ "@nuxt/kit": "^3.12.2",
35
+ "@nuxtjs/sitemap": "^5.2.1",
36
+ "defu": "^6.1.4",
37
+ "nuxt-link-checker": "3.0.0-rc.12",
38
+ "nuxt-og-image": "^3.0.0-rc.53",
39
+ "nuxt-schema-org": "^3.3.8",
40
+ "nuxt-seo-experiments": "4.0.0-rc.9",
41
+ "nuxt-simple-robots": "4.0.0-rc.19",
42
+ "nuxt-site-config": "^2.2.12",
43
+ "nuxt-site-config-kit": "^2.2.12",
44
+ "pkg-types": "^1.1.1",
45
+ "ufo": "^1.5.3"
38
46
  },
39
47
  "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"
48
+ "@antfu/eslint-config": "^2.21.1",
49
+ "@nuxt/module-builder": "^0.8.0",
50
+ "@nuxt/schema": "^3.12.2",
51
+ "@nuxt/test-utils": "3.13.1",
52
+ "@nuxt/ui": "^2.17.0",
53
+ "@nuxtjs/i18n": "^8.3.1",
54
+ "bumpp": "^9.4.1",
55
+ "eslint": "^9.5.0",
56
+ "execa": "^9.3.0",
57
+ "nitropack": "^2.9.6",
58
+ "nuxt": "^3.12.2",
59
+ "nuxt-icon": "^0.6.10",
60
+ "typescript": "5.4.5",
61
+ "vitest": "^1.6.0"
62
+ },
63
+ "build": {
64
+ "externals": [
65
+ "ofetch",
66
+ "consola/utils"
67
+ ]
53
68
  },
54
69
  "scripts": {
55
70
  "build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build",
56
71
  "dev": "nuxi dev .playground",
72
+ "dev:docs": "nuxi dev docs",
73
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare .playground",
57
74
  "lint": "eslint . --fix",
58
- "release": "bumpp && pnpm -r publish --access public",
59
- "test": "nuxi prepare .playground && true",
60
- "typecheck": "tsc --noEmit --strict"
75
+ "release": "pnpm build && bumpp && pnpm -r publish --access public",
76
+ "test": "nuxi prepare .playground && vitest",
77
+ "typecheck": "npx vue-tsc --noEmit --strict"
61
78
  }
62
79
  }
@@ -1,14 +0,0 @@
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
- });
@@ -1,49 +0,0 @@
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
- });