nuxtseo-shared 0.1.0

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/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) Harlan Wilton
4
+
5
+ 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:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ 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.
@@ -0,0 +1,21 @@
1
+ import * as vue from 'vue';
2
+ import * as nitropack from 'nitropack';
3
+ import { NuxtDevtoolsClient } from '@nuxt/devtools-kit/types';
4
+ import { $Fetch } from 'nitropack/types';
5
+
6
+ declare const appFetch: vue.Ref<$Fetch<unknown, nitropack.NitroFetchRequest> | undefined, $Fetch<unknown, nitropack.NitroFetchRequest> | undefined>;
7
+ declare const devtools: vue.Ref<NuxtDevtoolsClient | undefined, NuxtDevtoolsClient | undefined>;
8
+ declare const colorMode: vue.Ref<"dark" | "light", "dark" | "light">;
9
+ interface DevtoolsConnectionOptions {
10
+ onConnected?: (client: any) => void;
11
+ onRouteChange?: (route: any) => void;
12
+ }
13
+ /**
14
+ * Initialize the base devtools connection.
15
+ * Call this in your module's devtools client setup.
16
+ * Returns a cleanup function.
17
+ */
18
+ declare function useDevtoolsConnection(options?: DevtoolsConnectionOptions): void;
19
+
20
+ export { appFetch, colorMode, devtools, useDevtoolsConnection };
21
+ export type { DevtoolsConnectionOptions };
@@ -0,0 +1,21 @@
1
+ import * as vue from 'vue';
2
+ import * as nitropack from 'nitropack';
3
+ import { NuxtDevtoolsClient } from '@nuxt/devtools-kit/types';
4
+ import { $Fetch } from 'nitropack/types';
5
+
6
+ declare const appFetch: vue.Ref<$Fetch<unknown, nitropack.NitroFetchRequest> | undefined, $Fetch<unknown, nitropack.NitroFetchRequest> | undefined>;
7
+ declare const devtools: vue.Ref<NuxtDevtoolsClient | undefined, NuxtDevtoolsClient | undefined>;
8
+ declare const colorMode: vue.Ref<"dark" | "light", "dark" | "light">;
9
+ interface DevtoolsConnectionOptions {
10
+ onConnected?: (client: any) => void;
11
+ onRouteChange?: (route: any) => void;
12
+ }
13
+ /**
14
+ * Initialize the base devtools connection.
15
+ * Call this in your module's devtools client setup.
16
+ * Returns a cleanup function.
17
+ */
18
+ declare function useDevtoolsConnection(options?: DevtoolsConnectionOptions): void;
19
+
20
+ export { appFetch, colorMode, devtools, useDevtoolsConnection };
21
+ export type { DevtoolsConnectionOptions };
@@ -0,0 +1,25 @@
1
+ import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client';
2
+ import { ref, watchEffect } from 'vue';
3
+
4
+ const appFetch = ref();
5
+ const devtools = ref();
6
+ const colorMode = ref("dark");
7
+ function useDevtoolsConnection(options = {}) {
8
+ onDevtoolsClientConnected(async (client) => {
9
+ appFetch.value = client.host.app.$fetch;
10
+ watchEffect(() => {
11
+ colorMode.value = client.host.app.colorMode.value;
12
+ });
13
+ devtools.value = client.devtools;
14
+ options.onConnected?.(client);
15
+ if (options.onRouteChange) {
16
+ const $route = client.host.nuxt.vueApp.config.globalProperties?.$route;
17
+ options.onRouteChange($route);
18
+ client.host.nuxt.$router.afterEach((route) => {
19
+ options.onRouteChange(route);
20
+ });
21
+ }
22
+ });
23
+ }
24
+
25
+ export { appFetch, colorMode, devtools, useDevtoolsConnection };
@@ -0,0 +1,13 @@
1
+ import * as vue from 'vue';
2
+ import { MaybeRef, ComputedRef } from 'vue';
3
+ import { LanguageRegistration, HighlighterCore } from 'shiki';
4
+
5
+ declare const shiki: vue.Ref<HighlighterCore | undefined, HighlighterCore | undefined>;
6
+ interface LoadShikiOptions {
7
+ extraLangs?: (LanguageRegistration | Promise<LanguageRegistration>)[];
8
+ }
9
+ declare function loadShiki(options?: LoadShikiOptions): Promise<HighlighterCore>;
10
+ declare function useRenderCodeHighlight(code: MaybeRef<string>, lang: string): ComputedRef<string>;
11
+
12
+ export { loadShiki, shiki, useRenderCodeHighlight };
13
+ export type { LoadShikiOptions };
@@ -0,0 +1,13 @@
1
+ import * as vue from 'vue';
2
+ import { MaybeRef, ComputedRef } from 'vue';
3
+ import { LanguageRegistration, HighlighterCore } from 'shiki';
4
+
5
+ declare const shiki: vue.Ref<HighlighterCore | undefined, HighlighterCore | undefined>;
6
+ interface LoadShikiOptions {
7
+ extraLangs?: (LanguageRegistration | Promise<LanguageRegistration>)[];
8
+ }
9
+ declare function loadShiki(options?: LoadShikiOptions): Promise<HighlighterCore>;
10
+ declare function useRenderCodeHighlight(code: MaybeRef<string>, lang: string): ComputedRef<string>;
11
+
12
+ export { loadShiki, shiki, useRenderCodeHighlight };
13
+ export type { LoadShikiOptions };
@@ -0,0 +1,39 @@
1
+ import { createHighlighterCore } from 'shiki/core';
2
+ import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
3
+ import { ref, computed, toValue } from 'vue';
4
+ import { colorMode } from './rpc.mjs';
5
+ import '@nuxt/devtools-kit/iframe-client';
6
+
7
+ const shiki = ref();
8
+ async function loadShiki(options = {}) {
9
+ const langs = [
10
+ import('@shikijs/langs/xml'),
11
+ import('@shikijs/langs/json'),
12
+ import('@shikijs/langs/js')
13
+ ];
14
+ if (options.extraLangs) {
15
+ langs.push(...options.extraLangs);
16
+ }
17
+ shiki.value = await createHighlighterCore({
18
+ themes: [
19
+ import('@shikijs/themes/vitesse-light'),
20
+ import('@shikijs/themes/vitesse-dark')
21
+ ],
22
+ langs,
23
+ engine: createJavaScriptRegexEngine()
24
+ });
25
+ return shiki.value;
26
+ }
27
+ function useRenderCodeHighlight(code, lang) {
28
+ return computed(() => {
29
+ if (!shiki.value)
30
+ return "";
31
+ const theme = colorMode.value === "dark" ? "vitesse-dark" : "vitesse-light";
32
+ return shiki.value.codeToHtml(toValue(code) || "", {
33
+ lang,
34
+ theme
35
+ }) || "";
36
+ });
37
+ }
38
+
39
+ export { loadShiki, shiki, useRenderCodeHighlight };
@@ -0,0 +1,7 @@
1
+ export { appFetch, colorMode, devtools } from './composables/rpc.mjs';
2
+ export { loadShiki, shiki, useRenderCodeHighlight } from './composables/shiki.mjs';
3
+ import 'vue';
4
+ import 'nitropack';
5
+ import '@nuxt/devtools-kit/types';
6
+ import 'nitropack/types';
7
+ import 'shiki';
@@ -0,0 +1,7 @@
1
+ export { appFetch, colorMode, devtools } from './composables/rpc.js';
2
+ export { loadShiki, shiki, useRenderCodeHighlight } from './composables/shiki.js';
3
+ import 'vue';
4
+ import 'nitropack';
5
+ import '@nuxt/devtools-kit/types';
6
+ import 'nitropack/types';
7
+ import 'shiki';
@@ -0,0 +1,6 @@
1
+ export { appFetch, colorMode, devtools } from './composables/rpc.mjs';
2
+ export { loadShiki, shiki, useRenderCodeHighlight } from './composables/shiki.mjs';
3
+ import '@nuxt/devtools-kit/iframe-client';
4
+ import 'vue';
5
+ import 'shiki/core';
6
+ import 'shiki/engine/javascript';
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+
3
+ type ZodInstance = typeof z;
4
+ type ZodTypeAny = z.ZodTypeAny;
5
+ interface ContentSchemaOptions {
6
+ /**
7
+ * Pass the `z` instance from `@nuxt/content` to ensure `.editor()` works
8
+ * across Zod versions. When omitted, the module's bundled `z` is used.
9
+ */
10
+ z?: ZodInstance;
11
+ }
12
+ interface ContentEditorConfig {
13
+ hidden?: boolean;
14
+ input?: 'media' | 'icon' | 'textarea';
15
+ iconLibraries?: string[];
16
+ }
17
+ /**
18
+ * Apply Nuxt Content `.editor()` metadata to a zod schema field.
19
+ * No-ops gracefully when `.editor()` is not patched onto ZodType (outside Nuxt Content).
20
+ */
21
+ declare function withEditor<T extends ZodTypeAny>(schema: T, config: ContentEditorConfig): T;
22
+ /**
23
+ * Hide a zod schema field from the Nuxt Content / Studio editor.
24
+ * Only use for fields that genuinely don't work in a form (freeform JSON, deeply nested arrays).
25
+ */
26
+ declare function withEditorHidden<T extends ZodTypeAny>(schema: T): T;
27
+ interface DefineContentSchemaConfig<TSchema extends ZodTypeAny> {
28
+ /**
29
+ * The field name used in frontmatter (e.g. 'robots', 'sitemap', 'ogImage').
30
+ */
31
+ fieldName: string;
32
+ /**
33
+ * Build the zod schema for this field. Receives the zod instance
34
+ * (either the user's `@nuxt/content` patched version or the module's bundled one).
35
+ */
36
+ buildSchema: (z: ZodInstance) => TSchema;
37
+ /**
38
+ * Module label for deprecation warnings (e.g. 'robots', 'sitemap').
39
+ */
40
+ label: string;
41
+ /**
42
+ * Documentation URL for migration guidance.
43
+ */
44
+ docsUrl?: string;
45
+ }
46
+ /**
47
+ * Factory for creating a module's `define*Schema()` and deprecated `as*Collection()` exports.
48
+ *
49
+ * Each module provides its own schema builder. The factory handles:
50
+ * - Zod instance passthrough for `@nuxt/content` version compatibility
51
+ * - Consistent `DefineSchemaOptions` interface
52
+ * - Deprecated `asXxxCollection()` wrapper with migration warning
53
+ *
54
+ * @example
55
+ * // In @nuxtjs/robots/content.ts
56
+ * import { z } from 'zod'
57
+ * import { createContentSchemaFactory } from 'nuxtseo-shared/content'
58
+ *
59
+ * const { defineSchema, asCollection, schema } = createContentSchemaFactory({
60
+ * fieldName: 'robots',
61
+ * label: 'robots',
62
+ * docsUrl: 'https://nuxtseo.com/robots/guides/content',
63
+ * buildSchema: (z) => z.enum(['index, follow', 'noindex', 'nofollow', 'noindex, nofollow', 'none']).optional(),
64
+ * }, z)
65
+ *
66
+ * export { defineSchema as defineRobotsSchema, asCollection as asRobotsCollection, schema }
67
+ */
68
+ declare function createContentSchemaFactory<TSchema extends ZodTypeAny>(config: DefineContentSchemaConfig<TSchema>, defaultZ: ZodInstance): {
69
+ defineSchema: (options?: ContentSchemaOptions) => TSchema;
70
+ asCollection: <T>(collection: any) => T;
71
+ schema: z.ZodObject<{
72
+ [x: string]: TSchema;
73
+ }, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<{
74
+ [x: string]: TSchema;
75
+ }>, any> extends infer T ? { [k in keyof T]: T[k]; } : never, z.baseObjectInputType<{
76
+ [x: string]: TSchema;
77
+ }> extends infer T_1 ? { [k_1 in keyof T_1]: T_1[k_1]; } : never>;
78
+ fieldSchema: TSchema;
79
+ };
80
+
81
+ export { createContentSchemaFactory, withEditor, withEditorHidden };
82
+ export type { ContentEditorConfig, ContentSchemaOptions, DefineContentSchemaConfig };
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+
3
+ type ZodInstance = typeof z;
4
+ type ZodTypeAny = z.ZodTypeAny;
5
+ interface ContentSchemaOptions {
6
+ /**
7
+ * Pass the `z` instance from `@nuxt/content` to ensure `.editor()` works
8
+ * across Zod versions. When omitted, the module's bundled `z` is used.
9
+ */
10
+ z?: ZodInstance;
11
+ }
12
+ interface ContentEditorConfig {
13
+ hidden?: boolean;
14
+ input?: 'media' | 'icon' | 'textarea';
15
+ iconLibraries?: string[];
16
+ }
17
+ /**
18
+ * Apply Nuxt Content `.editor()` metadata to a zod schema field.
19
+ * No-ops gracefully when `.editor()` is not patched onto ZodType (outside Nuxt Content).
20
+ */
21
+ declare function withEditor<T extends ZodTypeAny>(schema: T, config: ContentEditorConfig): T;
22
+ /**
23
+ * Hide a zod schema field from the Nuxt Content / Studio editor.
24
+ * Only use for fields that genuinely don't work in a form (freeform JSON, deeply nested arrays).
25
+ */
26
+ declare function withEditorHidden<T extends ZodTypeAny>(schema: T): T;
27
+ interface DefineContentSchemaConfig<TSchema extends ZodTypeAny> {
28
+ /**
29
+ * The field name used in frontmatter (e.g. 'robots', 'sitemap', 'ogImage').
30
+ */
31
+ fieldName: string;
32
+ /**
33
+ * Build the zod schema for this field. Receives the zod instance
34
+ * (either the user's `@nuxt/content` patched version or the module's bundled one).
35
+ */
36
+ buildSchema: (z: ZodInstance) => TSchema;
37
+ /**
38
+ * Module label for deprecation warnings (e.g. 'robots', 'sitemap').
39
+ */
40
+ label: string;
41
+ /**
42
+ * Documentation URL for migration guidance.
43
+ */
44
+ docsUrl?: string;
45
+ }
46
+ /**
47
+ * Factory for creating a module's `define*Schema()` and deprecated `as*Collection()` exports.
48
+ *
49
+ * Each module provides its own schema builder. The factory handles:
50
+ * - Zod instance passthrough for `@nuxt/content` version compatibility
51
+ * - Consistent `DefineSchemaOptions` interface
52
+ * - Deprecated `asXxxCollection()` wrapper with migration warning
53
+ *
54
+ * @example
55
+ * // In @nuxtjs/robots/content.ts
56
+ * import { z } from 'zod'
57
+ * import { createContentSchemaFactory } from 'nuxtseo-shared/content'
58
+ *
59
+ * const { defineSchema, asCollection, schema } = createContentSchemaFactory({
60
+ * fieldName: 'robots',
61
+ * label: 'robots',
62
+ * docsUrl: 'https://nuxtseo.com/robots/guides/content',
63
+ * buildSchema: (z) => z.enum(['index, follow', 'noindex', 'nofollow', 'noindex, nofollow', 'none']).optional(),
64
+ * }, z)
65
+ *
66
+ * export { defineSchema as defineRobotsSchema, asCollection as asRobotsCollection, schema }
67
+ */
68
+ declare function createContentSchemaFactory<TSchema extends ZodTypeAny>(config: DefineContentSchemaConfig<TSchema>, defaultZ: ZodInstance): {
69
+ defineSchema: (options?: ContentSchemaOptions) => TSchema;
70
+ asCollection: <T>(collection: any) => T;
71
+ schema: z.ZodObject<{
72
+ [x: string]: TSchema;
73
+ }, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<{
74
+ [x: string]: TSchema;
75
+ }>, any> extends infer T ? { [k in keyof T]: T[k]; } : never, z.baseObjectInputType<{
76
+ [x: string]: TSchema;
77
+ }> extends infer T_1 ? { [k_1 in keyof T_1]: T_1[k_1]; } : never>;
78
+ fieldSchema: TSchema;
79
+ };
80
+
81
+ export { createContentSchemaFactory, withEditor, withEditorHidden };
82
+ export type { ContentEditorConfig, ContentSchemaOptions, DefineContentSchemaConfig };
@@ -0,0 +1,38 @@
1
+ function withEditor(schema, config) {
2
+ if (typeof schema.editor === "function")
3
+ return schema.editor(config);
4
+ return schema;
5
+ }
6
+ function withEditorHidden(schema) {
7
+ return withEditor(schema, { hidden: true });
8
+ }
9
+ function createContentSchemaFactory(config, defaultZ) {
10
+ const { fieldName, buildSchema, label, docsUrl } = config;
11
+ const defaultSchema = buildSchema(defaultZ);
12
+ const schemaObject = defaultZ.object({ [fieldName]: defaultSchema });
13
+ function defineSchema(options) {
14
+ const _z = options?.z ?? defaultZ;
15
+ if (_z === defaultZ)
16
+ return defaultSchema;
17
+ return buildSchema(_z);
18
+ }
19
+ function asCollection(collection) {
20
+ const migrationHint = docsUrl ? ` See ${docsUrl}` : "";
21
+ console.warn(`[${label}] \`as${capitalize(label)}Collection()\` is deprecated. Use \`define${capitalize(label)}Schema()\` in your collection schema instead.${migrationHint}`);
22
+ if (collection.type === "page") {
23
+ collection.schema = collection.schema ? schemaObject.extend(collection.schema.shape) : schemaObject;
24
+ }
25
+ return collection;
26
+ }
27
+ return {
28
+ defineSchema,
29
+ asCollection,
30
+ schema: schemaObject,
31
+ fieldSchema: defaultSchema
32
+ };
33
+ }
34
+ function capitalize(s) {
35
+ return s.charAt(0).toUpperCase() + s.slice(1);
36
+ }
37
+
38
+ export { createContentSchemaFactory, withEditor, withEditorHidden };
@@ -0,0 +1,14 @@
1
+ import { Resolver } from '@nuxt/kit';
2
+ import { Nuxt } from 'nuxt/schema';
3
+
4
+ interface DevToolsUIConfig {
5
+ route: string;
6
+ name: string;
7
+ title: string;
8
+ icon: string;
9
+ devPort?: number;
10
+ }
11
+ declare function setupDevToolsUI(config: DevToolsUIConfig, resolve: Resolver['resolve'], nuxt?: Nuxt): void;
12
+
13
+ export { setupDevToolsUI };
14
+ export type { DevToolsUIConfig };
@@ -0,0 +1,14 @@
1
+ import { Resolver } from '@nuxt/kit';
2
+ import { Nuxt } from 'nuxt/schema';
3
+
4
+ interface DevToolsUIConfig {
5
+ route: string;
6
+ name: string;
7
+ title: string;
8
+ icon: string;
9
+ devPort?: number;
10
+ }
11
+ declare function setupDevToolsUI(config: DevToolsUIConfig, resolve: Resolver['resolve'], nuxt?: Nuxt): void;
12
+
13
+ export { setupDevToolsUI };
14
+ export type { DevToolsUIConfig };
@@ -0,0 +1,46 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { addCustomTab } from '@nuxt/devtools-kit';
3
+ import { useNuxt } from '@nuxt/kit';
4
+
5
+ function setupDevToolsUI(config, resolve, nuxt = useNuxt()) {
6
+ const { route, name, title, icon, devPort = 3030 } = config;
7
+ const clientPath = resolve("./client");
8
+ const isProductionBuild = existsSync(clientPath);
9
+ if (isProductionBuild) {
10
+ nuxt.hook("vite:serverCreated", async (server) => {
11
+ const sirv = await import('sirv').then((r) => r.default || r);
12
+ server.middlewares.use(
13
+ route,
14
+ sirv(clientPath, { dev: true, single: true })
15
+ );
16
+ });
17
+ } else {
18
+ nuxt.hook("vite:extendConfig", (config2) => {
19
+ Object.assign(config2, {
20
+ server: {
21
+ ...config2.server,
22
+ proxy: {
23
+ ...config2.server?.proxy,
24
+ [route]: {
25
+ target: `http://localhost:${devPort}${route}`,
26
+ changeOrigin: true,
27
+ followRedirects: true,
28
+ rewrite: (p) => p.replace(route, "")
29
+ }
30
+ }
31
+ }
32
+ });
33
+ });
34
+ }
35
+ addCustomTab({
36
+ name,
37
+ title,
38
+ icon,
39
+ view: {
40
+ type: "iframe",
41
+ src: route
42
+ }
43
+ });
44
+ }
45
+
46
+ export { setupDevToolsUI };
@@ -0,0 +1,28 @@
1
+ import { LocaleObject, NuxtI18nOptions } from '@nuxtjs/i18n';
2
+
3
+ type Strategies = 'no_prefix' | 'prefix_except_default' | 'prefix' | 'prefix_and_default';
4
+ interface AutoI18nConfig {
5
+ locales: LocaleObject[];
6
+ defaultLocale: string;
7
+ strategy: Strategies;
8
+ differentDomains?: boolean;
9
+ pages?: Record<string, Record<string, string | false>>;
10
+ }
11
+ interface StrategyProps {
12
+ localeCode: string;
13
+ pageLocales: string;
14
+ nuxtI18nConfig: NuxtI18nOptions;
15
+ forcedStrategy?: Strategies;
16
+ normalisedLocales: AutoI18nConfig['locales'];
17
+ }
18
+ declare function generatePathForI18nPages(ctx: StrategyProps): string;
19
+ declare function splitPathForI18nLocales(path: string, autoI18n: AutoI18nConfig): string | string[];
20
+ declare function normalizeLocales(nuxtI18nConfig: NuxtI18nOptions): AutoI18nConfig['locales'];
21
+ declare function mapPathForI18nPages(path: string, autoI18n: AutoI18nConfig): string[] | false;
22
+ declare function resolveI18nConfig(logger?: {
23
+ warn: (msg: string) => void;
24
+ }): Promise<false | AutoI18nConfig>;
25
+ declare function mergeOnKey<T extends Record<string, any>>(arr: T[], key: keyof T): T[];
26
+
27
+ export { generatePathForI18nPages, mapPathForI18nPages, mergeOnKey, normalizeLocales, resolveI18nConfig, splitPathForI18nLocales };
28
+ export type { AutoI18nConfig, Strategies, StrategyProps };
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { LocaleObject, NuxtI18nOptions } from '@nuxtjs/i18n';
2
+
3
+ type Strategies = 'no_prefix' | 'prefix_except_default' | 'prefix' | 'prefix_and_default';
4
+ interface AutoI18nConfig {
5
+ locales: LocaleObject[];
6
+ defaultLocale: string;
7
+ strategy: Strategies;
8
+ differentDomains?: boolean;
9
+ pages?: Record<string, Record<string, string | false>>;
10
+ }
11
+ interface StrategyProps {
12
+ localeCode: string;
13
+ pageLocales: string;
14
+ nuxtI18nConfig: NuxtI18nOptions;
15
+ forcedStrategy?: Strategies;
16
+ normalisedLocales: AutoI18nConfig['locales'];
17
+ }
18
+ declare function generatePathForI18nPages(ctx: StrategyProps): string;
19
+ declare function splitPathForI18nLocales(path: string, autoI18n: AutoI18nConfig): string | string[];
20
+ declare function normalizeLocales(nuxtI18nConfig: NuxtI18nOptions): AutoI18nConfig['locales'];
21
+ declare function mapPathForI18nPages(path: string, autoI18n: AutoI18nConfig): string[] | false;
22
+ declare function resolveI18nConfig(logger?: {
23
+ warn: (msg: string) => void;
24
+ }): Promise<false | AutoI18nConfig>;
25
+ declare function mergeOnKey<T extends Record<string, any>>(arr: T[], key: keyof T): T[];
26
+
27
+ export { generatePathForI18nPages, mapPathForI18nPages, mergeOnKey, normalizeLocales, resolveI18nConfig, splitPathForI18nLocales };
28
+ export type { AutoI18nConfig, Strategies, StrategyProps };
package/dist/i18n.mjs ADDED
@@ -0,0 +1,121 @@
1
+ import { hasNuxtModule, getNuxtModuleVersion, hasNuxtModuleCompatibility } from '@nuxt/kit';
2
+ import { joinURL, withHttps, withBase, withLeadingSlash } from 'ufo';
3
+ import { getNuxtModuleOptions } from './kit.mjs';
4
+ import 'pathe';
5
+ import 'std-env';
6
+
7
+ const SLASH_PATTERN = /^\/|\/$/g;
8
+ function generatePathForI18nPages(ctx) {
9
+ const { localeCode, pageLocales, nuxtI18nConfig, forcedStrategy, normalisedLocales } = ctx;
10
+ const locale = normalisedLocales.find((l) => l.code === localeCode);
11
+ let path = pageLocales;
12
+ switch (forcedStrategy ?? nuxtI18nConfig.strategy) {
13
+ case "prefix_except_default":
14
+ case "prefix_and_default":
15
+ path = localeCode === nuxtI18nConfig.defaultLocale ? pageLocales : joinURL(localeCode, pageLocales);
16
+ break;
17
+ case "prefix":
18
+ path = joinURL(localeCode, pageLocales);
19
+ break;
20
+ }
21
+ return locale?.domain ? withHttps(withBase(path, locale.domain)) : path;
22
+ }
23
+ function splitPathForI18nLocales(path, autoI18n) {
24
+ const locales = autoI18n.strategy === "prefix_except_default" ? autoI18n.locales.filter((l) => l.code !== autoI18n.defaultLocale) : autoI18n.locales;
25
+ if (!path || path.startsWith("/_"))
26
+ return path;
27
+ const hasLocalePrefix = locales.some((l) => path.startsWith(`/${l.code}/`) || path === `/${l.code}`);
28
+ if (hasLocalePrefix)
29
+ return path;
30
+ return [
31
+ path,
32
+ ...locales.map((l) => `/${l.code}${path}`)
33
+ ];
34
+ }
35
+ function normalizeLocales(nuxtI18nConfig) {
36
+ const rawLocales = nuxtI18nConfig.locales || [];
37
+ let onlyLocales = nuxtI18nConfig?.bundle?.onlyLocales || [];
38
+ onlyLocales = typeof onlyLocales === "string" ? [onlyLocales] : onlyLocales;
39
+ let locales = mergeOnKey(rawLocales.map((locale) => typeof locale === "string" ? { code: locale } : locale), "code");
40
+ if (onlyLocales.length) {
41
+ locales = locales.filter((locale) => onlyLocales.includes(locale.code));
42
+ }
43
+ return locales.map((locale) => {
44
+ if (typeof locale.iso === "string" && !locale.language) {
45
+ locale.language = locale.iso;
46
+ }
47
+ const _hreflang = locale.language || locale.code;
48
+ const _sitemap = locale.language || locale.code;
49
+ return { ...locale, _hreflang, _sitemap };
50
+ });
51
+ }
52
+ function mapPathForI18nPages(path, autoI18n) {
53
+ const pages = autoI18n.pages;
54
+ if (!pages || !Object.keys(pages).length)
55
+ return false;
56
+ const withoutSlashes = path.replace(SLASH_PATTERN, "").replace("/index", "");
57
+ function resolveForAllLocales(pageName, pageLocales) {
58
+ return autoI18n.locales.filter((l) => {
59
+ if (l.code in pageLocales && pageLocales[l.code] === false)
60
+ return false;
61
+ if (autoI18n.strategy === "prefix_except_default" && l.code === autoI18n.defaultLocale)
62
+ return false;
63
+ return true;
64
+ }).map((l) => {
65
+ const localePath = l.code in pageLocales && pageLocales[l.code] !== false ? pageLocales[l.code] : `/${pageName}`;
66
+ return withLeadingSlash(generatePathForI18nPages({
67
+ localeCode: l.code,
68
+ pageLocales: localePath,
69
+ nuxtI18nConfig: { strategy: autoI18n.strategy, defaultLocale: autoI18n.defaultLocale },
70
+ normalisedLocales: autoI18n.locales
71
+ }));
72
+ });
73
+ }
74
+ if (withoutSlashes in pages) {
75
+ const pageLocales = pages[withoutSlashes];
76
+ if (pageLocales)
77
+ return resolveForAllLocales(withoutSlashes, pageLocales);
78
+ }
79
+ for (const [pageName, pageLocales] of Object.entries(pages)) {
80
+ if (!pageLocales)
81
+ continue;
82
+ if (autoI18n.defaultLocale in pageLocales && pageLocales[autoI18n.defaultLocale] === path)
83
+ return resolveForAllLocales(pageName, pageLocales);
84
+ }
85
+ return false;
86
+ }
87
+ async function resolveI18nConfig(logger) {
88
+ if (!hasNuxtModule("@nuxtjs/i18n"))
89
+ return false;
90
+ const i18nVersion = await getNuxtModuleVersion("@nuxtjs/i18n");
91
+ if (!await hasNuxtModuleCompatibility("@nuxtjs/i18n", ">=8")) {
92
+ logger?.warn(`You are using @nuxtjs/i18n v${i18nVersion}. For the best compatibility, please upgrade to @nuxtjs/i18n v8.0.0 or higher.`);
93
+ }
94
+ const nuxtI18nConfig = await getNuxtModuleOptions("@nuxtjs/i18n") || {};
95
+ const normalisedLocales = normalizeLocales(nuxtI18nConfig);
96
+ const usingI18nPages = Object.keys(nuxtI18nConfig.pages || {}).length;
97
+ const hasI18nConfigForAlternatives = nuxtI18nConfig.differentDomains || usingI18nPages || nuxtI18nConfig.strategy !== "no_prefix" && nuxtI18nConfig.locales;
98
+ if (!hasI18nConfigForAlternatives)
99
+ return false;
100
+ return {
101
+ differentDomains: nuxtI18nConfig.differentDomains,
102
+ defaultLocale: nuxtI18nConfig.defaultLocale,
103
+ locales: normalisedLocales,
104
+ strategy: nuxtI18nConfig.strategy,
105
+ pages: nuxtI18nConfig.pages
106
+ };
107
+ }
108
+ function mergeOnKey(arr, key) {
109
+ const map = /* @__PURE__ */ new Map();
110
+ for (const item of arr) {
111
+ const k = item[key];
112
+ if (map.has(k)) {
113
+ map.set(k, { ...map.get(k), ...item });
114
+ } else {
115
+ map.set(k, item);
116
+ }
117
+ }
118
+ return [...map.values()];
119
+ }
120
+
121
+ export { generatePathForI18nPages, mapPathForI18nPages, mergeOnKey, normalizeLocales, resolveI18nConfig, splitPathForI18nLocales };
@@ -0,0 +1,7 @@
1
+ export { setupDevToolsUI } from './devtools.mjs';
2
+ export { createNitroPromise, createPagesPromise, detectTarget, extendTypes, getNuxtModuleOptions, isNuxtGenerate, resolveNitroPreset } from './kit.mjs';
3
+ import '@nuxt/kit';
4
+ import 'nuxt/schema';
5
+ import '@nuxt/schema';
6
+ import 'nitropack';
7
+ import 'nitropack/types';
@@ -0,0 +1,7 @@
1
+ export { setupDevToolsUI } from './devtools.js';
2
+ export { createNitroPromise, createPagesPromise, detectTarget, extendTypes, getNuxtModuleOptions, isNuxtGenerate, resolveNitroPreset } from './kit.js';
3
+ import '@nuxt/kit';
4
+ import 'nuxt/schema';
5
+ import '@nuxt/schema';
6
+ import 'nitropack';
7
+ import 'nitropack/types';
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ export { setupDevToolsUI } from './devtools.mjs';
2
+ export { createNitroPromise, createPagesPromise, detectTarget, extendTypes, getNuxtModuleOptions, isNuxtGenerate, resolveNitroPreset } from './kit.mjs';
3
+ import 'node:fs';
4
+ import '@nuxt/devtools-kit';
5
+ import '@nuxt/kit';
6
+ import 'pathe';
7
+ import 'std-env';