@windrun-huaiin/third-ui 11.1.1 → 12.0.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/dist/clerk/clerk-optional-auth.d.ts +12 -0
- package/dist/clerk/clerk-optional-auth.js +33 -0
- package/dist/clerk/clerk-optional-auth.mjs +31 -0
- package/dist/clerk/clerk-provider-client.d.ts +3 -1
- package/dist/clerk/clerk-provider-client.js +22 -12
- package/dist/clerk/clerk-provider-client.mjs +22 -12
- package/dist/clerk/optional-auth.d.ts +12 -0
- package/dist/clerk/optional-auth.js +47 -0
- package/dist/clerk/optional-auth.mjs +45 -0
- package/dist/clerk/patch/optional-auth.d.ts +11 -0
- package/dist/clerk/patch/optional-auth.js +27 -0
- package/dist/clerk/patch/optional-auth.mjs +25 -0
- package/dist/fuma/base/custom-home-layout.d.ts +9 -1
- package/dist/fuma/base/custom-home-layout.js +2 -2
- package/dist/fuma/base/custom-home-layout.mjs +2 -2
- package/dist/fuma/fuma-page-genarator.d.ts +3 -1
- package/dist/fuma/fuma-page-genarator.js +8 -3
- package/dist/fuma/fuma-page-genarator.mjs +8 -3
- package/dist/lib/seo-util.d.ts +6 -2
- package/dist/lib/seo-util.js +21 -11
- package/dist/lib/seo-util.mjs +21 -11
- package/dist/main/footer.d.ts +6 -2
- package/dist/main/footer.js +3 -2
- package/dist/main/footer.mjs +3 -2
- package/package.json +8 -3
- package/src/clerk/clerk-provider-client.tsx +37 -12
- package/src/clerk/patch/optional-auth.ts +24 -0
- package/src/fuma/base/custom-home-layout.tsx +11 -1
- package/src/fuma/fuma-page-genarator.tsx +19 -4
- package/src/lib/seo-util.ts +27 -13
- package/src/main/footer.tsx +10 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { auth } from '@clerk/nextjs/server';
|
|
2
|
+
export type OptionalAuthResult = {
|
|
3
|
+
userId: string | null;
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
raw: Awaited<ReturnType<typeof auth>> | null;
|
|
6
|
+
hasClerkHeader: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 安全的可选鉴权:调用 auth(),若缺少 Clerk 标记或其他原因抛错则兜底返回 null。
|
|
10
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致页面崩溃。
|
|
11
|
+
*/
|
|
12
|
+
export declare function getOptionalAuth(): Promise<OptionalAuthResult>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tslib_es6 = require('../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
|
|
4
|
+
var server = require('@clerk/nextjs/server');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 安全的可选鉴权:调用 auth(),若缺少 Clerk 标记或其他原因抛错则兜底返回 null。
|
|
8
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致页面崩溃。
|
|
9
|
+
*/
|
|
10
|
+
function getOptionalAuth() {
|
|
11
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
try {
|
|
14
|
+
const res = yield server.auth();
|
|
15
|
+
return {
|
|
16
|
+
userId: (_a = res.userId) !== null && _a !== void 0 ? _a : null,
|
|
17
|
+
sessionId: (_b = res.sessionId) !== null && _b !== void 0 ? _b : null,
|
|
18
|
+
raw: res,
|
|
19
|
+
hasClerkHeader: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (_c) {
|
|
23
|
+
return {
|
|
24
|
+
userId: null,
|
|
25
|
+
sessionId: null,
|
|
26
|
+
raw: null,
|
|
27
|
+
hasClerkHeader: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.getOptionalAuth = getOptionalAuth;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
|
|
2
|
+
import { auth } from '@clerk/nextjs/server';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 安全的可选鉴权:调用 auth(),若缺少 Clerk 标记或其他原因抛错则兜底返回 null。
|
|
6
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致页面崩溃。
|
|
7
|
+
*/
|
|
8
|
+
function getOptionalAuth() {
|
|
9
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
try {
|
|
12
|
+
const res = yield auth();
|
|
13
|
+
return {
|
|
14
|
+
userId: (_a = res.userId) !== null && _a !== void 0 ? _a : null,
|
|
15
|
+
sessionId: (_b = res.sessionId) !== null && _b !== void 0 ? _b : null,
|
|
16
|
+
raw: res,
|
|
17
|
+
hasClerkHeader: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch (_c) {
|
|
21
|
+
return {
|
|
22
|
+
userId: null,
|
|
23
|
+
sessionId: null,
|
|
24
|
+
raw: null,
|
|
25
|
+
hasClerkHeader: false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { getOptionalAuth };
|
|
@@ -2,11 +2,13 @@ import React from 'react';
|
|
|
2
2
|
interface ClerkProviderClientProps {
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
locale: string;
|
|
5
|
+
localPrefixAsNeeded?: boolean;
|
|
6
|
+
defaultLocale?: string;
|
|
5
7
|
signInUrl?: string;
|
|
6
8
|
signUpUrl?: string;
|
|
7
9
|
fallbackSignInUrl?: string;
|
|
8
10
|
fallbackSignUpUrl?: string;
|
|
9
11
|
waitlistUrl?: string;
|
|
10
12
|
}
|
|
11
|
-
export declare function ClerkProviderClient({ children, locale, signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }: ClerkProviderClientProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function ClerkProviderClient({ children, locale, localPrefixAsNeeded, defaultLocale, signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }: ClerkProviderClientProps): import("react/jsx-runtime").JSX.Element;
|
|
12
14
|
export {};
|
|
@@ -5,27 +5,37 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
var clerkIntl = require('../lib/clerk-intl.js');
|
|
6
6
|
var nextjs = require('@clerk/nextjs');
|
|
7
7
|
|
|
8
|
-
function ClerkProviderClient({ children, locale, signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }) {
|
|
9
|
-
|
|
8
|
+
function ClerkProviderClient({ children, locale, localPrefixAsNeeded = true, defaultLocale = 'en', signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const currentLocalization = (_b = (_a = clerkIntl.clerkIntl[locale]) !== null && _a !== void 0 ? _a : clerkIntl.clerkIntl[defaultLocale]) !== null && _b !== void 0 ? _b : clerkIntl.clerkIntl.en;
|
|
11
|
+
// In as-needed mode, skip prefixing for the default locale so /sign-in stays unprefixed.
|
|
12
|
+
const shouldPrefixLocale = localPrefixAsNeeded ? locale !== defaultLocale : true;
|
|
13
|
+
const localeSegment = shouldPrefixLocale && locale ? `/${locale}` : '';
|
|
14
|
+
const buildUrl = (path) => path ? `${localeSegment}${path}` : undefined;
|
|
10
15
|
// build the ClerkProvider props, only add when the parameter is not empty
|
|
11
16
|
const clerkProviderProps = {
|
|
12
17
|
localization: currentLocalization,
|
|
13
18
|
};
|
|
14
19
|
// Only add URL when the parameter is not empty
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
const signInWithLocale = buildUrl(signInUrl);
|
|
21
|
+
if (signInWithLocale) {
|
|
22
|
+
clerkProviderProps.signInUrl = signInWithLocale;
|
|
17
23
|
}
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
const signUpWithLocale = buildUrl(signUpUrl);
|
|
25
|
+
if (signUpWithLocale) {
|
|
26
|
+
clerkProviderProps.signUpUrl = signUpWithLocale;
|
|
20
27
|
}
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const signInFallbackWithLocale = buildUrl(fallbackSignInUrl);
|
|
29
|
+
if (signInFallbackWithLocale) {
|
|
30
|
+
clerkProviderProps.signInFallbackRedirectUrl = signInFallbackWithLocale;
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
const signUpFallbackWithLocale = buildUrl(fallbackSignUpUrl);
|
|
33
|
+
if (signUpFallbackWithLocale) {
|
|
34
|
+
clerkProviderProps.signUpFallbackRedirectUrl = signUpFallbackWithLocale;
|
|
26
35
|
}
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
const waitlistWithLocale = buildUrl(waitlistUrl);
|
|
37
|
+
if (waitlistWithLocale) {
|
|
38
|
+
clerkProviderProps.waitlistUrl = waitlistWithLocale;
|
|
29
39
|
}
|
|
30
40
|
// console.log('ClerkProviderClient props:', clerkProviderProps);
|
|
31
41
|
return (jsxRuntime.jsx(nextjs.ClerkProvider, Object.assign({}, clerkProviderProps, { children: children })));
|
|
@@ -3,27 +3,37 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { clerkIntl } from '../lib/clerk-intl.mjs';
|
|
4
4
|
import { ClerkProvider } from '@clerk/nextjs';
|
|
5
5
|
|
|
6
|
-
function ClerkProviderClient({ children, locale, signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }) {
|
|
7
|
-
|
|
6
|
+
function ClerkProviderClient({ children, locale, localPrefixAsNeeded = true, defaultLocale = 'en', signInUrl, signUpUrl, fallbackSignInUrl, fallbackSignUpUrl, waitlistUrl, }) {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const currentLocalization = (_b = (_a = clerkIntl[locale]) !== null && _a !== void 0 ? _a : clerkIntl[defaultLocale]) !== null && _b !== void 0 ? _b : clerkIntl.en;
|
|
9
|
+
// In as-needed mode, skip prefixing for the default locale so /sign-in stays unprefixed.
|
|
10
|
+
const shouldPrefixLocale = localPrefixAsNeeded ? locale !== defaultLocale : true;
|
|
11
|
+
const localeSegment = shouldPrefixLocale && locale ? `/${locale}` : '';
|
|
12
|
+
const buildUrl = (path) => path ? `${localeSegment}${path}` : undefined;
|
|
8
13
|
// build the ClerkProvider props, only add when the parameter is not empty
|
|
9
14
|
const clerkProviderProps = {
|
|
10
15
|
localization: currentLocalization,
|
|
11
16
|
};
|
|
12
17
|
// Only add URL when the parameter is not empty
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
const signInWithLocale = buildUrl(signInUrl);
|
|
19
|
+
if (signInWithLocale) {
|
|
20
|
+
clerkProviderProps.signInUrl = signInWithLocale;
|
|
15
21
|
}
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
const signUpWithLocale = buildUrl(signUpUrl);
|
|
23
|
+
if (signUpWithLocale) {
|
|
24
|
+
clerkProviderProps.signUpUrl = signUpWithLocale;
|
|
18
25
|
}
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
const signInFallbackWithLocale = buildUrl(fallbackSignInUrl);
|
|
27
|
+
if (signInFallbackWithLocale) {
|
|
28
|
+
clerkProviderProps.signInFallbackRedirectUrl = signInFallbackWithLocale;
|
|
21
29
|
}
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
const signUpFallbackWithLocale = buildUrl(fallbackSignUpUrl);
|
|
31
|
+
if (signUpFallbackWithLocale) {
|
|
32
|
+
clerkProviderProps.signUpFallbackRedirectUrl = signUpFallbackWithLocale;
|
|
24
33
|
}
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
const waitlistWithLocale = buildUrl(waitlistUrl);
|
|
35
|
+
if (waitlistWithLocale) {
|
|
36
|
+
clerkProviderProps.waitlistUrl = waitlistWithLocale;
|
|
27
37
|
}
|
|
28
38
|
// console.log('ClerkProviderClient props:', clerkProviderProps);
|
|
29
39
|
return (jsx(ClerkProvider, Object.assign({}, clerkProviderProps, { children: children })));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { auth } from '@clerk/nextjs/server';
|
|
2
|
+
export type OptionalAuthResult = {
|
|
3
|
+
userId: string | null;
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
raw: Awaited<ReturnType<typeof auth>> | null;
|
|
6
|
+
hasClerkHeader: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 安全的可选鉴权:只有在请求头已包含 Clerk 标记时才调用 auth()。
|
|
10
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致 auth() 抛错。
|
|
11
|
+
*/
|
|
12
|
+
export declare function getOptionalAuth(): Promise<OptionalAuthResult>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tslib_es6 = require('../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
|
|
4
|
+
var server = require('@clerk/nextjs/server');
|
|
5
|
+
var headers = require('next/headers');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 安全的可选鉴权:只有在请求头已包含 Clerk 标记时才调用 auth()。
|
|
9
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致 auth() 抛错。
|
|
10
|
+
*/
|
|
11
|
+
function getOptionalAuth() {
|
|
12
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
var _a, _b, _c;
|
|
14
|
+
const h = yield headers.headers();
|
|
15
|
+
const hasClerkHeader = Boolean(h.get('x-clerk-auth-status')) ||
|
|
16
|
+
Boolean(h.get('x-clerk-user-id')) ||
|
|
17
|
+
Boolean(h.get('authorization')) ||
|
|
18
|
+
Boolean((_a = h.get('cookie')) === null || _a === void 0 ? void 0 : _a.includes('__session'));
|
|
19
|
+
if (!hasClerkHeader) {
|
|
20
|
+
return {
|
|
21
|
+
userId: null,
|
|
22
|
+
sessionId: null,
|
|
23
|
+
raw: null,
|
|
24
|
+
hasClerkHeader: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const res = yield server.auth();
|
|
29
|
+
return {
|
|
30
|
+
userId: (_b = res.userId) !== null && _b !== void 0 ? _b : null,
|
|
31
|
+
sessionId: (_c = res.sessionId) !== null && _c !== void 0 ? _c : null,
|
|
32
|
+
raw: res,
|
|
33
|
+
hasClerkHeader: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (_d) {
|
|
37
|
+
return {
|
|
38
|
+
userId: null,
|
|
39
|
+
sessionId: null,
|
|
40
|
+
raw: null,
|
|
41
|
+
hasClerkHeader: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
exports.getOptionalAuth = getOptionalAuth;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
|
|
2
|
+
import { auth } from '@clerk/nextjs/server';
|
|
3
|
+
import { headers } from 'next/headers';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 安全的可选鉴权:只有在请求头已包含 Clerk 标记时才调用 auth()。
|
|
7
|
+
* 用于公开页/可选登录场景,避免因缺少中间件标记导致 auth() 抛错。
|
|
8
|
+
*/
|
|
9
|
+
function getOptionalAuth() {
|
|
10
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
11
|
+
var _a, _b, _c;
|
|
12
|
+
const h = yield headers();
|
|
13
|
+
const hasClerkHeader = Boolean(h.get('x-clerk-auth-status')) ||
|
|
14
|
+
Boolean(h.get('x-clerk-user-id')) ||
|
|
15
|
+
Boolean(h.get('authorization')) ||
|
|
16
|
+
Boolean((_a = h.get('cookie')) === null || _a === void 0 ? void 0 : _a.includes('__session'));
|
|
17
|
+
if (!hasClerkHeader) {
|
|
18
|
+
return {
|
|
19
|
+
userId: null,
|
|
20
|
+
sessionId: null,
|
|
21
|
+
raw: null,
|
|
22
|
+
hasClerkHeader: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const res = yield auth();
|
|
27
|
+
return {
|
|
28
|
+
userId: (_b = res.userId) !== null && _b !== void 0 ? _b : null,
|
|
29
|
+
sessionId: (_c = res.sessionId) !== null && _c !== void 0 ? _c : null,
|
|
30
|
+
raw: res,
|
|
31
|
+
hasClerkHeader: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (_d) {
|
|
35
|
+
return {
|
|
36
|
+
userId: null,
|
|
37
|
+
sessionId: null,
|
|
38
|
+
raw: null,
|
|
39
|
+
hasClerkHeader: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { getOptionalAuth };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { auth } from '@clerk/nextjs/server';
|
|
2
|
+
export type OptionalAuthResult = {
|
|
3
|
+
userId: string | null;
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
raw: Awaited<ReturnType<typeof auth>> | null;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* 可选鉴权:在缺少 Clerk 标记或未登录时返回 null,避免 auth() 抛错。
|
|
9
|
+
* 仅供服务端使用,请从 @third-ui/clerk/patch/optional-auth 导入。
|
|
10
|
+
*/
|
|
11
|
+
export declare function getOptionalAuth(): Promise<OptionalAuthResult>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tslib_es6 = require('../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js');
|
|
4
|
+
var server = require('@clerk/nextjs/server');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 可选鉴权:在缺少 Clerk 标记或未登录时返回 null,避免 auth() 抛错。
|
|
8
|
+
* 仅供服务端使用,请从 @third-ui/clerk/patch/optional-auth 导入。
|
|
9
|
+
*/
|
|
10
|
+
function getOptionalAuth() {
|
|
11
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
try {
|
|
14
|
+
const res = yield server.auth();
|
|
15
|
+
return {
|
|
16
|
+
userId: (_a = res.userId) !== null && _a !== void 0 ? _a : null,
|
|
17
|
+
sessionId: (_b = res.sessionId) !== null && _b !== void 0 ? _b : null,
|
|
18
|
+
raw: res,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (_c) {
|
|
22
|
+
return { userId: null, sessionId: null, raw: null };
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.getOptionalAuth = getOptionalAuth;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
|
|
2
|
+
import { auth } from '@clerk/nextjs/server';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 可选鉴权:在缺少 Clerk 标记或未登录时返回 null,避免 auth() 抛错。
|
|
6
|
+
* 仅供服务端使用,请从 @third-ui/clerk/patch/optional-auth 导入。
|
|
7
|
+
*/
|
|
8
|
+
function getOptionalAuth() {
|
|
9
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
try {
|
|
12
|
+
const res = yield auth();
|
|
13
|
+
return {
|
|
14
|
+
userId: (_a = res.userId) !== null && _a !== void 0 ? _a : null,
|
|
15
|
+
sessionId: (_b = res.sessionId) !== null && _b !== void 0 ? _b : null,
|
|
16
|
+
raw: res,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (_c) {
|
|
20
|
+
return { userId: null, sessionId: null, raw: null };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { getOptionalAuth };
|
|
@@ -72,6 +72,14 @@ export interface CustomHomeLayoutProps {
|
|
|
72
72
|
* Customize the order of header action items.
|
|
73
73
|
*/
|
|
74
74
|
actionOrders?: HeaderActionOrders;
|
|
75
|
+
/**
|
|
76
|
+
* Whether localePrefix is set to 'as-needed' (default: true)
|
|
77
|
+
*/
|
|
78
|
+
localPrefixAsNeeded?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The default locale for the application (default: 'en')
|
|
81
|
+
*/
|
|
82
|
+
defaultLocale?: string;
|
|
75
83
|
children?: ReactNode;
|
|
76
84
|
}
|
|
77
85
|
export interface HeaderActionOrders {
|
|
@@ -79,7 +87,7 @@ export interface HeaderActionOrders {
|
|
|
79
87
|
mobileBar?: MobileBarAction[];
|
|
80
88
|
mobileMenu?: MobileMenuAction[];
|
|
81
89
|
}
|
|
82
|
-
export declare function CustomHomeLayout({ locale, options, children, showBanner, bannerHeight, headerHeight, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter, showGoToTop, style, floatingNav, actionOrders, }: CustomHomeLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
90
|
+
export declare function CustomHomeLayout({ locale, options, children, showBanner, bannerHeight, headerHeight, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter, showGoToTop, style, floatingNav, actionOrders, localPrefixAsNeeded, defaultLocale, }: CustomHomeLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
83
91
|
export declare function HomeTitle({ children, className, }: {
|
|
84
92
|
children: ReactNode;
|
|
85
93
|
className?: string;
|
|
@@ -8,7 +8,7 @@ var footer = require('../../main/footer.js');
|
|
|
8
8
|
var goToTop = require('../../main/go-to-top.js');
|
|
9
9
|
var customHeader = require('./custom-header.js');
|
|
10
10
|
|
|
11
|
-
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, }) {
|
|
11
|
+
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localPrefixAsNeeded = true, defaultLocale = 'en', }) {
|
|
12
12
|
const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
|
|
13
13
|
const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
|
|
14
14
|
const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-nav-height': `${headerHeight}rem`, paddingTop: floatingNav
|
|
@@ -17,7 +17,7 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
|
|
|
17
17
|
const { nav } = options, homeLayoutProps = tslib_es6.__rest(options, ["nav"]);
|
|
18
18
|
const navOptions = nav !== null && nav !== void 0 ? nav : {};
|
|
19
19
|
const header = (jsxRuntime.jsx(customHeader.CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
|
|
20
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsxRuntime.jsx(fumaBannerSuit.FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxRuntime.jsxs(home.HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer$1 !== null && footer$1 !== void 0 ? footer$1 : jsxRuntime.jsx(footer.Footer, { locale: locale }) : null, showGoToTop ? goToTop$1 !== null && goToTop$1 !== void 0 ? goToTop$1 : jsxRuntime.jsx(goToTop.GoToTop, {}) : null] }))] }));
|
|
20
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsxRuntime.jsx(fumaBannerSuit.FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxRuntime.jsxs(home.HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer$1 !== null && footer$1 !== void 0 ? footer$1 : jsxRuntime.jsx(footer.Footer, { locale: locale, localPrefixAsNeeded: localPrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop$1 !== null && goToTop$1 !== void 0 ? goToTop$1 : jsxRuntime.jsx(goToTop.GoToTop, {}) : null] }))] }));
|
|
21
21
|
}
|
|
22
22
|
function HomeTitle({ children, className, }) {
|
|
23
23
|
return (jsxRuntime.jsx("span", { className: `font-medium in-[.uwu]:hidden in-[header]:text-[clamp(12px,3vw,15px)]! ${className !== null && className !== void 0 ? className : ''}`, children: children }));
|
|
@@ -6,7 +6,7 @@ import { Footer } from '../../main/footer.mjs';
|
|
|
6
6
|
import { GoToTop } from '../../main/go-to-top.mjs';
|
|
7
7
|
import { CustomHomeHeader } from './custom-header.mjs';
|
|
8
8
|
|
|
9
|
-
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, }) {
|
|
9
|
+
function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localPrefixAsNeeded = true, defaultLocale = 'en', }) {
|
|
10
10
|
const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
|
|
11
11
|
const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
|
|
12
12
|
const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-nav-height': `${headerHeight}rem`, paddingTop: floatingNav
|
|
@@ -15,7 +15,7 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
|
|
|
15
15
|
const { nav } = options, homeLayoutProps = __rest(options, ["nav"]);
|
|
16
16
|
const navOptions = nav !== null && nav !== void 0 ? nav : {};
|
|
17
17
|
const header = (jsx(CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
|
|
18
|
-
return (jsxs(Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsx(FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxs(HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer !== null && footer !== void 0 ? footer : jsx(Footer, { locale: locale }) : null, showGoToTop ? goToTop !== null && goToTop !== void 0 ? goToTop : jsx(GoToTop, {}) : null] }))] }));
|
|
18
|
+
return (jsxs(Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsx(FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxs(HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer !== null && footer !== void 0 ? footer : jsx(Footer, { locale: locale, localPrefixAsNeeded: localPrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop !== null && goToTop !== void 0 ? goToTop : jsx(GoToTop, {}) : null] }))] }));
|
|
19
19
|
}
|
|
20
20
|
function HomeTitle({ children, className, }) {
|
|
21
21
|
return (jsx("span", { className: `font-medium in-[.uwu]:hidden in-[header]:text-[clamp(12px,3vw,15px)]! ${className !== null && className !== void 0 ? className : ''}`, children: children }));
|
|
@@ -15,8 +15,10 @@ interface FumaPageParams {
|
|
|
15
15
|
showBreadcrumb?: boolean;
|
|
16
16
|
showTableOfContent?: boolean;
|
|
17
17
|
showTableOfContentPopover?: boolean;
|
|
18
|
+
localPrefixAsNeeded?: boolean;
|
|
19
|
+
defaultLocale?: string;
|
|
18
20
|
}
|
|
19
|
-
export declare function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales, showBreadcrumb, showTableOfContent, showTableOfContentPopover, }: FumaPageParams): {
|
|
21
|
+
export declare function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales, showBreadcrumb, showTableOfContent, showTableOfContentPopover, localPrefixAsNeeded, defaultLocale, }: FumaPageParams): {
|
|
20
22
|
Page: ({ params }: {
|
|
21
23
|
params: Promise<{
|
|
22
24
|
locale: string;
|
|
@@ -5,13 +5,16 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
var page = require('fumadocs-ui/page');
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var tocFooterWrapper = require('./mdx/toc-footer-wrapper.js');
|
|
8
|
+
var lib = require('@windrun-huaiin/lib');
|
|
8
9
|
|
|
9
|
-
function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales = ['en'], showBreadcrumb = true, showTableOfContent = true, showTableOfContentPopover = false, }) {
|
|
10
|
+
function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales = ['en'], showBreadcrumb = true, showTableOfContent = true, showTableOfContentPopover = false, localPrefixAsNeeded = true, defaultLocale = 'en', }) {
|
|
10
11
|
const Page = function Page(_a) {
|
|
11
12
|
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ params }) {
|
|
13
|
+
var _b, _c, _d;
|
|
12
14
|
const { slug, locale } = yield params;
|
|
13
15
|
const page$1 = mdxContentSource.getPage(slug, locale);
|
|
14
16
|
if (!page$1) {
|
|
17
|
+
console.log('[FumaPage] missing page', { slug, locale, available: (_d = (_c = (_b = mdxContentSource.pageTree) === null || _b === void 0 ? void 0 : _b[locale]) === null || _c === void 0 ? void 0 : _c.children) === null || _d === void 0 ? void 0 : _d.map((c) => c.url) });
|
|
15
18
|
return jsxRuntime.jsx(FallbackPage, { siteIcon: siteIcon });
|
|
16
19
|
}
|
|
17
20
|
const path = githubBaseUrl ? `${mdxSourceDir}/${page$1.path}` : undefined;
|
|
@@ -47,11 +50,13 @@ function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSour
|
|
|
47
50
|
const baseRoute = mdxSourceDir.replace('src/mdx/', '');
|
|
48
51
|
// build the current page path
|
|
49
52
|
const currentPath = slug ? slug.join('/') : '';
|
|
50
|
-
const
|
|
53
|
+
const localizedPath = lib.getAsNeededLocalizedUrl(locale || defaultLocale, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
54
|
+
const currentUrl = `${baseUrl}${localizedPath}`;
|
|
51
55
|
// generate the seo language map
|
|
52
56
|
const seoLanguageMap = {};
|
|
53
57
|
supportedLocales.forEach(loc => {
|
|
54
|
-
|
|
58
|
+
const seoPath = lib.getAsNeededLocalizedUrl(loc, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
59
|
+
seoLanguageMap[loc] = `${baseUrl}${seoPath}`;
|
|
55
60
|
});
|
|
56
61
|
return {
|
|
57
62
|
metadataBase: new URL(baseUrl),
|
|
@@ -3,13 +3,16 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
3
3
|
import { DocsPage, DocsTitle, DocsDescription, DocsBody } from 'fumadocs-ui/page';
|
|
4
4
|
import { cloneElement } from 'react';
|
|
5
5
|
import { TocFooterWrapper } from './mdx/toc-footer-wrapper.mjs';
|
|
6
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
6
7
|
|
|
7
|
-
function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales = ['en'], showBreadcrumb = true, showTableOfContent = true, showTableOfContentPopover = false, }) {
|
|
8
|
+
function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSourceDir, githubBaseUrl, copyButtonComponent, siteIcon, FallbackPage, supportedLocales = ['en'], showBreadcrumb = true, showTableOfContent = true, showTableOfContentPopover = false, localPrefixAsNeeded = true, defaultLocale = 'en', }) {
|
|
8
9
|
const Page = function Page(_a) {
|
|
9
10
|
return __awaiter(this, arguments, void 0, function* ({ params }) {
|
|
11
|
+
var _b, _c, _d;
|
|
10
12
|
const { slug, locale } = yield params;
|
|
11
13
|
const page = mdxContentSource.getPage(slug, locale);
|
|
12
14
|
if (!page) {
|
|
15
|
+
console.log('[FumaPage] missing page', { slug, locale, available: (_d = (_c = (_b = mdxContentSource.pageTree) === null || _b === void 0 ? void 0 : _b[locale]) === null || _c === void 0 ? void 0 : _c.children) === null || _d === void 0 ? void 0 : _d.map((c) => c.url) });
|
|
13
16
|
return jsx(FallbackPage, { siteIcon: siteIcon });
|
|
14
17
|
}
|
|
15
18
|
const path = githubBaseUrl ? `${mdxSourceDir}/${page.path}` : undefined;
|
|
@@ -45,11 +48,13 @@ function createFumaPage({ sourceKey, mdxContentSource, getMDXComponents, mdxSour
|
|
|
45
48
|
const baseRoute = mdxSourceDir.replace('src/mdx/', '');
|
|
46
49
|
// build the current page path
|
|
47
50
|
const currentPath = slug ? slug.join('/') : '';
|
|
48
|
-
const
|
|
51
|
+
const localizedPath = getAsNeededLocalizedUrl(locale || defaultLocale, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
52
|
+
const currentUrl = `${baseUrl}${localizedPath}`;
|
|
49
53
|
// generate the seo language map
|
|
50
54
|
const seoLanguageMap = {};
|
|
51
55
|
supportedLocales.forEach(loc => {
|
|
52
|
-
|
|
56
|
+
const seoPath = getAsNeededLocalizedUrl(loc, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
57
|
+
seoLanguageMap[loc] = `${baseUrl}${seoPath}`;
|
|
53
58
|
});
|
|
54
59
|
return {
|
|
55
60
|
metadataBase: new URL(baseUrl),
|
package/dist/lib/seo-util.d.ts
CHANGED
|
@@ -11,9 +11,11 @@ export declare function generateRobots(baseUrl: string): MetadataRoute.Robots;
|
|
|
11
11
|
* @param locales - Supported locales array
|
|
12
12
|
* @param mdxSourceDir - MDX source directory path
|
|
13
13
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
14
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
15
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
14
16
|
* @returns Sitemap entries
|
|
15
17
|
*/
|
|
16
|
-
export declare function generateSitemap(baseUrl: string, locales: string[], mdxSourceDir: string, openMdxSEOSiteMap?: boolean): MetadataRoute.Sitemap;
|
|
18
|
+
export declare function generateSitemap(baseUrl: string, locales: string[], mdxSourceDir: string, openMdxSEOSiteMap?: boolean, localPrefixAsNeeded?: boolean, defaultLocale?: string): MetadataRoute.Sitemap;
|
|
17
19
|
/**
|
|
18
20
|
* Create robots.txt handler function
|
|
19
21
|
* @param baseUrl - The base URL of the website
|
|
@@ -26,6 +28,8 @@ export declare function createRobotsHandler(baseUrl: string): () => MetadataRout
|
|
|
26
28
|
* @param locales - Supported locales array
|
|
27
29
|
* @param mdxSourceDir - MDX source directory path, default is empty
|
|
28
30
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
31
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
32
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
29
33
|
* @returns Sitemap handler function
|
|
30
34
|
*/
|
|
31
|
-
export declare function createSitemapHandler(baseUrl: string, locales: string[], mdxSourceDir?: string, openMdxSEOSiteMap?: boolean): () => MetadataRoute.Sitemap;
|
|
35
|
+
export declare function createSitemapHandler(baseUrl: string, locales: string[], mdxSourceDir?: string, openMdxSEOSiteMap?: boolean, localPrefixAsNeeded?: boolean, defaultLocale?: string): () => MetadataRoute.Sitemap;
|
package/dist/lib/seo-util.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var fs = require('fs');
|
|
4
4
|
var path = require('path');
|
|
5
|
+
var lib = require('@windrun-huaiin/lib');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Generate robots.txt content
|
|
@@ -23,9 +24,11 @@ function generateRobots(baseUrl) {
|
|
|
23
24
|
* @param locales - Supported locales array
|
|
24
25
|
* @param mdxSourceDir - MDX source directory path
|
|
25
26
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
27
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
28
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
26
29
|
* @returns Sitemap entries
|
|
27
30
|
*/
|
|
28
|
-
function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = true) {
|
|
31
|
+
function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = true, localPrefixAsNeeded = true, defaultLocale = 'en') {
|
|
29
32
|
// 2. handle index.mdx (blog start page) and other slugs
|
|
30
33
|
const blogRoutes = [];
|
|
31
34
|
// 1. read all blog mdx file names with error handling
|
|
@@ -38,8 +41,9 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
38
41
|
for (const locale of locales) {
|
|
39
42
|
for (const f of blogFiles) {
|
|
40
43
|
if (f === 'index.mdx') {
|
|
44
|
+
const localizedPath = lib.getAsNeededLocalizedUrl(locale, '/blog', localPrefixAsNeeded, defaultLocale);
|
|
41
45
|
blogRoutes.push({
|
|
42
|
-
url: `${baseUrl}
|
|
46
|
+
url: `${baseUrl}${localizedPath}`,
|
|
43
47
|
lastModified: new Date(),
|
|
44
48
|
changeFrequency: 'daily',
|
|
45
49
|
priority: 1.0
|
|
@@ -47,8 +51,9 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
47
51
|
}
|
|
48
52
|
else {
|
|
49
53
|
const slug = f.replace(/\.mdx$/, '');
|
|
54
|
+
const localizedPath = lib.getAsNeededLocalizedUrl(locale, `/blog/${slug}`, localPrefixAsNeeded, defaultLocale);
|
|
50
55
|
blogRoutes.push({
|
|
51
|
-
url: `${baseUrl}
|
|
56
|
+
url: `${baseUrl}${localizedPath}`,
|
|
52
57
|
lastModified: new Date(),
|
|
53
58
|
changeFrequency: f === 'ioc.mdx' ? 'daily' : 'monthly',
|
|
54
59
|
priority: 0.8
|
|
@@ -64,12 +69,15 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
// 3. main page (all language versions)
|
|
67
|
-
const mainRoutes = locales.map(locale =>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
const mainRoutes = locales.map(locale => {
|
|
73
|
+
const localizedPath = lib.getAsNeededLocalizedUrl(locale, '/', localPrefixAsNeeded, defaultLocale);
|
|
74
|
+
return {
|
|
75
|
+
url: `${baseUrl}${localizedPath}`,
|
|
76
|
+
lastModified: new Date(),
|
|
77
|
+
changeFrequency: 'weekly',
|
|
78
|
+
priority: 1.0
|
|
79
|
+
};
|
|
80
|
+
});
|
|
73
81
|
return openMdxSEOSiteMap ? [...mainRoutes, ...blogRoutes] : [...mainRoutes];
|
|
74
82
|
}
|
|
75
83
|
/**
|
|
@@ -88,12 +96,14 @@ function createRobotsHandler(baseUrl) {
|
|
|
88
96
|
* @param locales - Supported locales array
|
|
89
97
|
* @param mdxSourceDir - MDX source directory path, default is empty
|
|
90
98
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
99
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
100
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
91
101
|
* @returns Sitemap handler function
|
|
92
102
|
*/
|
|
93
|
-
function createSitemapHandler(baseUrl, locales, mdxSourceDir = '', openMdxSEOSiteMap = true) {
|
|
103
|
+
function createSitemapHandler(baseUrl, locales, mdxSourceDir = '', openMdxSEOSiteMap = true, localPrefixAsNeeded = true, defaultLocale = 'en') {
|
|
94
104
|
// force static generation
|
|
95
105
|
const sitemapHandler = function sitemap() {
|
|
96
|
-
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap);
|
|
106
|
+
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap, localPrefixAsNeeded, defaultLocale);
|
|
97
107
|
};
|
|
98
108
|
// Add static generation directive
|
|
99
109
|
sitemapHandler.dynamic = 'force-static';
|
package/dist/lib/seo-util.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Generate robots.txt content
|
|
@@ -21,9 +22,11 @@ function generateRobots(baseUrl) {
|
|
|
21
22
|
* @param locales - Supported locales array
|
|
22
23
|
* @param mdxSourceDir - MDX source directory path
|
|
23
24
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
25
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
26
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
24
27
|
* @returns Sitemap entries
|
|
25
28
|
*/
|
|
26
|
-
function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = true) {
|
|
29
|
+
function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = true, localPrefixAsNeeded = true, defaultLocale = 'en') {
|
|
27
30
|
// 2. handle index.mdx (blog start page) and other slugs
|
|
28
31
|
const blogRoutes = [];
|
|
29
32
|
// 1. read all blog mdx file names with error handling
|
|
@@ -36,8 +39,9 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
36
39
|
for (const locale of locales) {
|
|
37
40
|
for (const f of blogFiles) {
|
|
38
41
|
if (f === 'index.mdx') {
|
|
42
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, '/blog', localPrefixAsNeeded, defaultLocale);
|
|
39
43
|
blogRoutes.push({
|
|
40
|
-
url: `${baseUrl}
|
|
44
|
+
url: `${baseUrl}${localizedPath}`,
|
|
41
45
|
lastModified: new Date(),
|
|
42
46
|
changeFrequency: 'daily',
|
|
43
47
|
priority: 1.0
|
|
@@ -45,8 +49,9 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
45
49
|
}
|
|
46
50
|
else {
|
|
47
51
|
const slug = f.replace(/\.mdx$/, '');
|
|
52
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, `/blog/${slug}`, localPrefixAsNeeded, defaultLocale);
|
|
48
53
|
blogRoutes.push({
|
|
49
|
-
url: `${baseUrl}
|
|
54
|
+
url: `${baseUrl}${localizedPath}`,
|
|
50
55
|
lastModified: new Date(),
|
|
51
56
|
changeFrequency: f === 'ioc.mdx' ? 'daily' : 'monthly',
|
|
52
57
|
priority: 0.8
|
|
@@ -62,12 +67,15 @@ function generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap = tru
|
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
// 3. main page (all language versions)
|
|
65
|
-
const mainRoutes = locales.map(locale =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const mainRoutes = locales.map(locale => {
|
|
71
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, '/', localPrefixAsNeeded, defaultLocale);
|
|
72
|
+
return {
|
|
73
|
+
url: `${baseUrl}${localizedPath}`,
|
|
74
|
+
lastModified: new Date(),
|
|
75
|
+
changeFrequency: 'weekly',
|
|
76
|
+
priority: 1.0
|
|
77
|
+
};
|
|
78
|
+
});
|
|
71
79
|
return openMdxSEOSiteMap ? [...mainRoutes, ...blogRoutes] : [...mainRoutes];
|
|
72
80
|
}
|
|
73
81
|
/**
|
|
@@ -86,12 +94,14 @@ function createRobotsHandler(baseUrl) {
|
|
|
86
94
|
* @param locales - Supported locales array
|
|
87
95
|
* @param mdxSourceDir - MDX source directory path, default is empty
|
|
88
96
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
97
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
98
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
89
99
|
* @returns Sitemap handler function
|
|
90
100
|
*/
|
|
91
|
-
function createSitemapHandler(baseUrl, locales, mdxSourceDir = '', openMdxSEOSiteMap = true) {
|
|
101
|
+
function createSitemapHandler(baseUrl, locales, mdxSourceDir = '', openMdxSEOSiteMap = true, localPrefixAsNeeded = true, defaultLocale = 'en') {
|
|
92
102
|
// force static generation
|
|
93
103
|
const sitemapHandler = function sitemap() {
|
|
94
|
-
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap);
|
|
104
|
+
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap, localPrefixAsNeeded, defaultLocale);
|
|
95
105
|
};
|
|
96
106
|
// Add static generation directive
|
|
97
107
|
sitemapHandler.dynamic = 'force-static';
|
package/dist/main/footer.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
interface FooterProps {
|
|
2
2
|
locale: string;
|
|
3
|
-
|
|
3
|
+
localPrefixAsNeeded?: boolean;
|
|
4
|
+
defaultLocale?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function Footer({ locale, localPrefixAsNeeded, defaultLocale }: FooterProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
7
|
+
export {};
|
package/dist/main/footer.js
CHANGED
|
@@ -7,9 +7,10 @@ var server$1 = require('@windrun-huaiin/base-ui/components/server');
|
|
|
7
7
|
var Link = require('next/link');
|
|
8
8
|
var footerEmail = require('./footer-email.js');
|
|
9
9
|
var tIntl = require('../lib/t-intl.js');
|
|
10
|
+
var lib = require('@windrun-huaiin/lib');
|
|
10
11
|
|
|
11
12
|
function Footer(_a) {
|
|
12
|
-
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale }) {
|
|
13
|
+
return tslib_es6.__awaiter(this, arguments, void 0, function* ({ locale, localPrefixAsNeeded = true, defaultLocale = 'en' }) {
|
|
13
14
|
const tFooter = yield server.getTranslations({ locale, namespace: 'footer' });
|
|
14
15
|
const company = tIntl.safeT(tFooter, 'company', '');
|
|
15
16
|
const data = {
|
|
@@ -21,7 +22,7 @@ function Footer(_a) {
|
|
|
21
22
|
clickToCopyText: tIntl.safeT(tFooter, 'clickToCopy', 'Click to copy'),
|
|
22
23
|
copiedText: tIntl.safeT(tFooter, 'copied', 'Copied!'),
|
|
23
24
|
};
|
|
24
|
-
return (jsxRuntime.jsx("div", { className: "mb-10 w-full mx-auto border-t-purple-700/80 border-t", children: jsxRuntime.jsx("footer", { children: jsxRuntime.jsxs("div", { className: "w-full flex flex-col items-center justify-center px-4 py-8 space-y-3", children: [jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-x-2 gap-y-2 text-xs sm:text-sm sm:gap-x-6", children: [jsxRuntime.jsxs(Link, { href:
|
|
25
|
+
return (jsxRuntime.jsx("div", { className: "mb-10 w-full mx-auto border-t-purple-700/80 border-t", children: jsxRuntime.jsx("footer", { children: jsxRuntime.jsxs("div", { className: "w-full flex flex-col items-center justify-center px-4 py-8 space-y-3", children: [jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-x-2 gap-y-2 text-xs sm:text-sm sm:gap-x-6", children: [jsxRuntime.jsxs(Link, { href: lib.getAsNeededLocalizedUrl(locale, "/legal/terms", localPrefixAsNeeded, defaultLocale), className: "flex items-center space-x-1 hover:underline", children: [jsxRuntime.jsx(server$1.globalLucideIcons.ReceiptText, { className: "h-3.5 w-3.5" }), jsxRuntime.jsx("span", { children: data.terms })] }), jsxRuntime.jsxs(Link, { href: lib.getAsNeededLocalizedUrl(locale, "/legal/privacy", localPrefixAsNeeded, defaultLocale), className: "flex items-center space-x-1 hover:underline", children: [jsxRuntime.jsx(server$1.globalLucideIcons.ShieldUser, { className: "h-3.5 w-3.5" }), jsxRuntime.jsx("span", { children: data.privacy })] }), jsxRuntime.jsxs(footerEmail.FooterEmail, { email: data.email, clickToCopyText: data.clickToCopyText, copiedText: data.copiedText, children: [jsxRuntime.jsx(server$1.globalLucideIcons.Mail, { className: "h-3.5 w-3.5" }), jsxRuntime.jsx("span", { children: data.contactUs })] })] }), jsxRuntime.jsx("div", { className: "text-xs sm:text-sm text-center", children: jsxRuntime.jsx("span", { children: data.copyright }) })] }) }) }));
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
|
package/dist/main/footer.mjs
CHANGED
|
@@ -5,9 +5,10 @@ import { globalLucideIcons } from '@windrun-huaiin/base-ui/components/server';
|
|
|
5
5
|
import Link from 'next/link';
|
|
6
6
|
import { FooterEmail } from './footer-email.mjs';
|
|
7
7
|
import { safeT } from '../lib/t-intl.mjs';
|
|
8
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
8
9
|
|
|
9
10
|
function Footer(_a) {
|
|
10
|
-
return __awaiter(this, arguments, void 0, function* ({ locale }) {
|
|
11
|
+
return __awaiter(this, arguments, void 0, function* ({ locale, localPrefixAsNeeded = true, defaultLocale = 'en' }) {
|
|
11
12
|
const tFooter = yield getTranslations({ locale, namespace: 'footer' });
|
|
12
13
|
const company = safeT(tFooter, 'company', '');
|
|
13
14
|
const data = {
|
|
@@ -19,7 +20,7 @@ function Footer(_a) {
|
|
|
19
20
|
clickToCopyText: safeT(tFooter, 'clickToCopy', 'Click to copy'),
|
|
20
21
|
copiedText: safeT(tFooter, 'copied', 'Copied!'),
|
|
21
22
|
};
|
|
22
|
-
return (jsx("div", { className: "mb-10 w-full mx-auto border-t-purple-700/80 border-t", children: jsx("footer", { children: jsxs("div", { className: "w-full flex flex-col items-center justify-center px-4 py-8 space-y-3", children: [jsxs("div", { className: "flex flex-wrap items-center justify-center gap-x-2 gap-y-2 text-xs sm:text-sm sm:gap-x-6", children: [jsxs(Link, { href:
|
|
23
|
+
return (jsx("div", { className: "mb-10 w-full mx-auto border-t-purple-700/80 border-t", children: jsx("footer", { children: jsxs("div", { className: "w-full flex flex-col items-center justify-center px-4 py-8 space-y-3", children: [jsxs("div", { className: "flex flex-wrap items-center justify-center gap-x-2 gap-y-2 text-xs sm:text-sm sm:gap-x-6", children: [jsxs(Link, { href: getAsNeededLocalizedUrl(locale, "/legal/terms", localPrefixAsNeeded, defaultLocale), className: "flex items-center space-x-1 hover:underline", children: [jsx(globalLucideIcons.ReceiptText, { className: "h-3.5 w-3.5" }), jsx("span", { children: data.terms })] }), jsxs(Link, { href: getAsNeededLocalizedUrl(locale, "/legal/privacy", localPrefixAsNeeded, defaultLocale), className: "flex items-center space-x-1 hover:underline", children: [jsx(globalLucideIcons.ShieldUser, { className: "h-3.5 w-3.5" }), jsx("span", { children: data.privacy })] }), jsxs(FooterEmail, { email: data.email, clickToCopyText: data.clickToCopyText, copiedText: data.copiedText, children: [jsx(globalLucideIcons.Mail, { className: "h-3.5 w-3.5" }), jsx("span", { children: data.contactUs })] })] }), jsx("div", { className: "text-xs sm:text-sm text-center", children: jsx("span", { children: data.copyright }) })] }) }) }));
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/third-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.1",
|
|
4
4
|
"description": "Third-party integrated UI components for windrun-huaiin projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"import": "./dist/clerk/server.mjs",
|
|
17
17
|
"require": "./dist/clerk/server.js"
|
|
18
18
|
},
|
|
19
|
+
"./clerk/patch/optional-auth": {
|
|
20
|
+
"types": "./dist/clerk/patch/optional-auth.d.ts",
|
|
21
|
+
"import": "./dist/clerk/patch/optional-auth.mjs",
|
|
22
|
+
"require": "./dist/clerk/patch/optional-auth.js"
|
|
23
|
+
},
|
|
19
24
|
"./fingerprint": {
|
|
20
25
|
"types": "./dist/clerk/fingerprint/index.d.ts",
|
|
21
26
|
"import": "./dist/clerk/fingerprint/index.mjs",
|
|
@@ -81,8 +86,8 @@
|
|
|
81
86
|
"react-medium-image-zoom": "^5.2.14",
|
|
82
87
|
"swiper": "^12.0.3",
|
|
83
88
|
"zod": "^4.1.12",
|
|
84
|
-
"@windrun-huaiin/base-ui": "^
|
|
85
|
-
"@windrun-huaiin/lib": "^
|
|
89
|
+
"@windrun-huaiin/base-ui": "^12.0.0",
|
|
90
|
+
"@windrun-huaiin/lib": "^12.0.0"
|
|
86
91
|
},
|
|
87
92
|
"peerDependencies": {
|
|
88
93
|
"clsx": "^2.1.1",
|
|
@@ -7,6 +7,10 @@ import React from 'react';
|
|
|
7
7
|
interface ClerkProviderClientProps {
|
|
8
8
|
children: React.ReactNode;
|
|
9
9
|
locale: string;
|
|
10
|
+
// Whether localePrefix is set to 'as-needed' (default: true)
|
|
11
|
+
localPrefixAsNeeded?: boolean;
|
|
12
|
+
// The default locale used by the host app (default: 'en')
|
|
13
|
+
defaultLocale?: string;
|
|
10
14
|
signInUrl?: string;
|
|
11
15
|
signUpUrl?: string;
|
|
12
16
|
fallbackSignInUrl?: string;
|
|
@@ -17,13 +21,25 @@ interface ClerkProviderClientProps {
|
|
|
17
21
|
export function ClerkProviderClient({
|
|
18
22
|
children,
|
|
19
23
|
locale,
|
|
24
|
+
localPrefixAsNeeded = true,
|
|
25
|
+
defaultLocale = 'en',
|
|
20
26
|
signInUrl,
|
|
21
27
|
signUpUrl,
|
|
22
28
|
fallbackSignInUrl,
|
|
23
29
|
fallbackSignUpUrl,
|
|
24
30
|
waitlistUrl,
|
|
25
31
|
}: ClerkProviderClientProps) {
|
|
26
|
-
const currentLocalization =
|
|
32
|
+
const currentLocalization =
|
|
33
|
+
clerkIntl[locale as keyof typeof clerkIntl] ??
|
|
34
|
+
clerkIntl[defaultLocale as keyof typeof clerkIntl] ??
|
|
35
|
+
clerkIntl.en;
|
|
36
|
+
|
|
37
|
+
// In as-needed mode, skip prefixing for the default locale so /sign-in stays unprefixed.
|
|
38
|
+
const shouldPrefixLocale = localPrefixAsNeeded ? locale !== defaultLocale : true;
|
|
39
|
+
const localeSegment = shouldPrefixLocale && locale ? `/${locale}` : '';
|
|
40
|
+
|
|
41
|
+
const buildUrl = (path?: string) =>
|
|
42
|
+
path ? `${localeSegment}${path}` : undefined;
|
|
27
43
|
|
|
28
44
|
// build the ClerkProvider props, only add when the parameter is not empty
|
|
29
45
|
const clerkProviderProps: Record<string, any> = {
|
|
@@ -31,20 +47,29 @@ export function ClerkProviderClient({
|
|
|
31
47
|
};
|
|
32
48
|
|
|
33
49
|
// Only add URL when the parameter is not empty
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
const signInWithLocale = buildUrl(signInUrl);
|
|
51
|
+
if (signInWithLocale) {
|
|
52
|
+
clerkProviderProps.signInUrl = signInWithLocale;
|
|
36
53
|
}
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
|
|
55
|
+
const signUpWithLocale = buildUrl(signUpUrl);
|
|
56
|
+
if (signUpWithLocale) {
|
|
57
|
+
clerkProviderProps.signUpUrl = signUpWithLocale;
|
|
39
58
|
}
|
|
40
|
-
|
|
41
|
-
|
|
59
|
+
|
|
60
|
+
const signInFallbackWithLocale = buildUrl(fallbackSignInUrl);
|
|
61
|
+
if (signInFallbackWithLocale) {
|
|
62
|
+
clerkProviderProps.signInFallbackRedirectUrl = signInFallbackWithLocale;
|
|
42
63
|
}
|
|
43
|
-
|
|
44
|
-
|
|
64
|
+
|
|
65
|
+
const signUpFallbackWithLocale = buildUrl(fallbackSignUpUrl);
|
|
66
|
+
if (signUpFallbackWithLocale) {
|
|
67
|
+
clerkProviderProps.signUpFallbackRedirectUrl = signUpFallbackWithLocale;
|
|
45
68
|
}
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
|
|
70
|
+
const waitlistWithLocale = buildUrl(waitlistUrl);
|
|
71
|
+
if (waitlistWithLocale) {
|
|
72
|
+
clerkProviderProps.waitlistUrl = waitlistWithLocale;
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
// console.log('ClerkProviderClient props:', clerkProviderProps);
|
|
@@ -54,4 +79,4 @@ export function ClerkProviderClient({
|
|
|
54
79
|
{children}
|
|
55
80
|
</ClerkProvider>
|
|
56
81
|
);
|
|
57
|
-
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { auth } from '@clerk/nextjs/server';
|
|
2
|
+
|
|
3
|
+
export type OptionalAuthResult = {
|
|
4
|
+
userId: string | null;
|
|
5
|
+
sessionId: string | null;
|
|
6
|
+
raw: Awaited<ReturnType<typeof auth>> | null;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 可选鉴权:在缺少 Clerk 标记或未登录时返回 null,避免 auth() 抛错。
|
|
11
|
+
* 仅供服务端使用,请从 @third-ui/clerk/patch/optional-auth 导入。
|
|
12
|
+
*/
|
|
13
|
+
export async function getOptionalAuth(): Promise<OptionalAuthResult> {
|
|
14
|
+
try {
|
|
15
|
+
const res = await auth();
|
|
16
|
+
return {
|
|
17
|
+
userId: res.userId ?? null,
|
|
18
|
+
sessionId: res.sessionId ?? null,
|
|
19
|
+
raw: res,
|
|
20
|
+
};
|
|
21
|
+
} catch {
|
|
22
|
+
return { userId: null, sessionId: null, raw: null };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -86,6 +86,14 @@ export interface CustomHomeLayoutProps {
|
|
|
86
86
|
* Customize the order of header action items.
|
|
87
87
|
*/
|
|
88
88
|
actionOrders?: HeaderActionOrders;
|
|
89
|
+
/**
|
|
90
|
+
* Whether localePrefix is set to 'as-needed' (default: true)
|
|
91
|
+
*/
|
|
92
|
+
localPrefixAsNeeded?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* The default locale for the application (default: 'en')
|
|
95
|
+
*/
|
|
96
|
+
defaultLocale?: string;
|
|
89
97
|
children?: ReactNode;
|
|
90
98
|
}
|
|
91
99
|
|
|
@@ -112,6 +120,8 @@ export function CustomHomeLayout({
|
|
|
112
120
|
style,
|
|
113
121
|
floatingNav = false,
|
|
114
122
|
actionOrders,
|
|
123
|
+
localPrefixAsNeeded = true,
|
|
124
|
+
defaultLocale = 'en',
|
|
115
125
|
}: CustomHomeLayoutProps) {
|
|
116
126
|
const resolvedBannerHeight = bannerHeight ?? (showBanner ? 3 : 0.5);
|
|
117
127
|
const resolvedPaddingTop =
|
|
@@ -161,7 +171,7 @@ export function CustomHomeLayout({
|
|
|
161
171
|
style={layoutStyle}
|
|
162
172
|
>
|
|
163
173
|
{children}
|
|
164
|
-
{showFooter ? footer ?? <Footer locale={locale} /> : null}
|
|
174
|
+
{showFooter ? footer ?? <Footer locale={locale} localPrefixAsNeeded={localPrefixAsNeeded} defaultLocale={defaultLocale} /> : null}
|
|
165
175
|
{showGoToTop ? goToTop ?? <GoToTop /> : null}
|
|
166
176
|
</HomeLayout>
|
|
167
177
|
</>
|
|
@@ -2,6 +2,7 @@ import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page
|
|
|
2
2
|
import { ReactNode, ReactElement, cloneElement } from 'react';
|
|
3
3
|
import { TocFooterWrapper } from '@third-ui/fuma/mdx/toc-footer-wrapper';
|
|
4
4
|
import type { LLMCopyButtonProps, LLMCopyButton } from '@third-ui/fuma/mdx/toc-base';
|
|
5
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
5
6
|
|
|
6
7
|
interface FumaPageParams {
|
|
7
8
|
/*
|
|
@@ -36,7 +37,7 @@ interface FumaPageParams {
|
|
|
36
37
|
* The fallback page component to use when the page is not found
|
|
37
38
|
*/
|
|
38
39
|
FallbackPage: React.ComponentType<{ siteIcon: ReactNode }>;
|
|
39
|
-
/*
|
|
40
|
+
/*
|
|
40
41
|
* Supported locales for generating alternates metadata, defaults to ['en']
|
|
41
42
|
*/
|
|
42
43
|
supportedLocales?: string[];
|
|
@@ -49,6 +50,16 @@ interface FumaPageParams {
|
|
|
49
50
|
|
|
50
51
|
// default false, for mobile style can cause issue
|
|
51
52
|
showTableOfContentPopover?: boolean;
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Whether localePrefix is set to 'as-needed' (default: true)
|
|
56
|
+
*/
|
|
57
|
+
localPrefixAsNeeded?: boolean;
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* The default locale for the application (default: 'en')
|
|
61
|
+
*/
|
|
62
|
+
defaultLocale?: string;
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
export function createFumaPage({
|
|
@@ -64,11 +75,14 @@ export function createFumaPage({
|
|
|
64
75
|
showBreadcrumb = true,
|
|
65
76
|
showTableOfContent = true,
|
|
66
77
|
showTableOfContentPopover = false,
|
|
78
|
+
localPrefixAsNeeded = true,
|
|
79
|
+
defaultLocale = 'en',
|
|
67
80
|
}: FumaPageParams) {
|
|
68
81
|
const Page = async function Page({ params }: { params: Promise<{ locale: string; slug?: string[] }> }) {
|
|
69
82
|
const { slug, locale } = await params;
|
|
70
83
|
const page = mdxContentSource.getPage(slug, locale);
|
|
71
84
|
if (!page) {
|
|
85
|
+
console.log('[FumaPage] missing page', { slug, locale, available: mdxContentSource.pageTree?.[locale]?.children?.map((c: any) => c.url) });
|
|
72
86
|
return <FallbackPage siteIcon={siteIcon} />;
|
|
73
87
|
}
|
|
74
88
|
|
|
@@ -129,14 +143,15 @@ export function createFumaPage({
|
|
|
129
143
|
const baseRoute = mdxSourceDir.replace('src/mdx/', '');
|
|
130
144
|
// build the current page path
|
|
131
145
|
const currentPath = slug ? slug.join('/') : '';
|
|
132
|
-
const
|
|
146
|
+
const localizedPath = getAsNeededLocalizedUrl(locale || defaultLocale, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
147
|
+
const currentUrl = `${baseUrl}${localizedPath}`;
|
|
133
148
|
|
|
134
149
|
// generate the seo language map
|
|
135
150
|
const seoLanguageMap: Record<string, string> = {};
|
|
136
151
|
|
|
137
|
-
|
|
138
152
|
supportedLocales.forEach(loc => {
|
|
139
|
-
|
|
153
|
+
const seoPath = getAsNeededLocalizedUrl(loc, `/${baseRoute}${currentPath ? `/${currentPath}` : ''}`, localPrefixAsNeeded, defaultLocale);
|
|
154
|
+
seoLanguageMap[loc] = `${baseUrl}${seoPath}`;
|
|
140
155
|
});
|
|
141
156
|
|
|
142
157
|
return {
|
package/src/lib/seo-util.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { MetadataRoute } from 'next';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Generate robots.txt content
|
|
@@ -23,13 +24,17 @@ export function generateRobots(baseUrl: string): MetadataRoute.Robots {
|
|
|
23
24
|
* @param locales - Supported locales array
|
|
24
25
|
* @param mdxSourceDir - MDX source directory path
|
|
25
26
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
27
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
28
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
26
29
|
* @returns Sitemap entries
|
|
27
30
|
*/
|
|
28
31
|
export function generateSitemap(
|
|
29
32
|
baseUrl: string,
|
|
30
33
|
locales: string[],
|
|
31
34
|
mdxSourceDir: string,
|
|
32
|
-
openMdxSEOSiteMap: boolean = true
|
|
35
|
+
openMdxSEOSiteMap: boolean = true,
|
|
36
|
+
localPrefixAsNeeded: boolean = true,
|
|
37
|
+
defaultLocale: string = 'en'
|
|
33
38
|
): MetadataRoute.Sitemap {
|
|
34
39
|
// 2. handle index.mdx (blog start page) and other slugs
|
|
35
40
|
const blogRoutes: MetadataRoute.Sitemap = [];
|
|
@@ -46,16 +51,18 @@ export function generateSitemap(
|
|
|
46
51
|
for (const locale of locales) {
|
|
47
52
|
for (const f of blogFiles) {
|
|
48
53
|
if (f === 'index.mdx') {
|
|
54
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, '/blog', localPrefixAsNeeded, defaultLocale);
|
|
49
55
|
blogRoutes.push({
|
|
50
|
-
url: `${baseUrl}
|
|
56
|
+
url: `${baseUrl}${localizedPath}`,
|
|
51
57
|
lastModified: new Date(),
|
|
52
58
|
changeFrequency: 'daily',
|
|
53
59
|
priority: 1.0
|
|
54
60
|
});
|
|
55
61
|
} else {
|
|
56
62
|
const slug = f.replace(/\.mdx$/, '');
|
|
63
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, `/blog/${slug}`, localPrefixAsNeeded, defaultLocale);
|
|
57
64
|
blogRoutes.push({
|
|
58
|
-
url: `${baseUrl}
|
|
65
|
+
url: `${baseUrl}${localizedPath}`,
|
|
59
66
|
lastModified: new Date(),
|
|
60
67
|
changeFrequency: f === 'ioc.mdx' ? 'daily' : 'monthly',
|
|
61
68
|
priority: 0.8
|
|
@@ -71,12 +78,15 @@ export function generateSitemap(
|
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
// 3. main page (all language versions)
|
|
74
|
-
const mainRoutes = locales.map(locale =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
const mainRoutes = locales.map(locale => {
|
|
82
|
+
const localizedPath = getAsNeededLocalizedUrl(locale, '/', localPrefixAsNeeded, defaultLocale);
|
|
83
|
+
return {
|
|
84
|
+
url: `${baseUrl}${localizedPath}`,
|
|
85
|
+
lastModified: new Date(),
|
|
86
|
+
changeFrequency: 'weekly' as const,
|
|
87
|
+
priority: 1.0
|
|
88
|
+
};
|
|
89
|
+
});
|
|
80
90
|
|
|
81
91
|
return openMdxSEOSiteMap ? [...mainRoutes, ...blogRoutes] : [...mainRoutes];
|
|
82
92
|
}
|
|
@@ -98,21 +108,25 @@ export function createRobotsHandler(baseUrl: string) {
|
|
|
98
108
|
* @param locales - Supported locales array
|
|
99
109
|
* @param mdxSourceDir - MDX source directory path, default is empty
|
|
100
110
|
* @param openMdxSEOSiteMap - Whether to include MDX content in sitemap, default is true
|
|
111
|
+
* @param localPrefixAsNeeded - Whether localePrefix is set to 'as-needed' (default: true)
|
|
112
|
+
* @param defaultLocale - The default locale for the application (default: 'en')
|
|
101
113
|
* @returns Sitemap handler function
|
|
102
114
|
*/
|
|
103
115
|
export function createSitemapHandler(
|
|
104
116
|
baseUrl: string,
|
|
105
117
|
locales: string[],
|
|
106
118
|
mdxSourceDir: string = '',
|
|
107
|
-
openMdxSEOSiteMap: boolean = true
|
|
119
|
+
openMdxSEOSiteMap: boolean = true,
|
|
120
|
+
localPrefixAsNeeded: boolean = true,
|
|
121
|
+
defaultLocale: string = 'en'
|
|
108
122
|
) {
|
|
109
123
|
// force static generation
|
|
110
124
|
const sitemapHandler = function sitemap(): MetadataRoute.Sitemap {
|
|
111
|
-
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap);
|
|
125
|
+
return generateSitemap(baseUrl, locales, mdxSourceDir, openMdxSEOSiteMap, localPrefixAsNeeded, defaultLocale);
|
|
112
126
|
};
|
|
113
|
-
|
|
127
|
+
|
|
114
128
|
// Add static generation directive
|
|
115
129
|
(sitemapHandler as any).dynamic = 'force-static';
|
|
116
|
-
|
|
130
|
+
|
|
117
131
|
return sitemapHandler;
|
|
118
132
|
}
|
package/src/main/footer.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { globalLucideIcons as icons } from '@windrun-huaiin/base-ui/components/s
|
|
|
3
3
|
import Link from "next/link";
|
|
4
4
|
import { FooterEmail } from './footer-email';
|
|
5
5
|
import { safeT } from '../lib/t-intl';
|
|
6
|
+
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib';
|
|
6
7
|
|
|
7
8
|
interface FooterData {
|
|
8
9
|
terms: string;
|
|
@@ -15,7 +16,13 @@ interface FooterData {
|
|
|
15
16
|
copiedText: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
interface FooterProps {
|
|
20
|
+
locale: string;
|
|
21
|
+
localPrefixAsNeeded?: boolean;
|
|
22
|
+
defaultLocale?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function Footer({ locale, localPrefixAsNeeded = true, defaultLocale = 'en' }: FooterProps) {
|
|
19
26
|
const tFooter = await getTranslations({ locale, namespace: 'footer' });
|
|
20
27
|
|
|
21
28
|
const company = safeT(tFooter, 'company', '');
|
|
@@ -36,11 +43,11 @@ export async function Footer({ locale }: { locale: string }) {
|
|
|
36
43
|
<footer>
|
|
37
44
|
<div className="w-full flex flex-col items-center justify-center px-4 py-8 space-y-3">
|
|
38
45
|
<div className="flex flex-wrap items-center justify-center gap-x-2 gap-y-2 text-xs sm:text-sm sm:gap-x-6">
|
|
39
|
-
<Link href={
|
|
46
|
+
<Link href={getAsNeededLocalizedUrl(locale, "/legal/terms", localPrefixAsNeeded, defaultLocale)} className="flex items-center space-x-1 hover:underline">
|
|
40
47
|
<icons.ReceiptText className="h-3.5 w-3.5"/>
|
|
41
48
|
<span>{data.terms}</span>
|
|
42
49
|
</Link>
|
|
43
|
-
<Link href={
|
|
50
|
+
<Link href={getAsNeededLocalizedUrl(locale, "/legal/privacy", localPrefixAsNeeded, defaultLocale)} className="flex items-center space-x-1 hover:underline">
|
|
44
51
|
<icons.ShieldUser className="h-3.5 w-3.5"/>
|
|
45
52
|
<span>{data.privacy}</span>
|
|
46
53
|
</Link>
|