refactor: route next/link through compat re-export (#33632)

This commit is contained in:
yyh 2026-03-18 12:14:59 +08:00 committed by GitHub
parent 6100acb780
commit 69d1ccb7a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
86 changed files with 94 additions and 89 deletions

View File

@ -1,7 +1,6 @@
'use client'
import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -10,9 +9,10 @@ import Input from '@/app/components/base/input'
import Toast from '@/app/components/base/toast'
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
import { sendResetPasswordCode } from '@/service/common'
export default function CheckCode() {

View File

@ -1,6 +1,5 @@
'use client'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -10,6 +9,7 @@ import Toast from '@/app/components/base/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import { useWebAppStore } from '@/context/web-app-context'
import Link from '@/next/link'
import { webAppLogin } from '@/service/common'
import { fetchAccessToken } from '@/service/share'
import { setWebAppAccessToken, setWebAppPassport } from '@/service/webapp-auth'

View File

@ -1,12 +1,12 @@
'use client'
import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
import { LicenseStatus } from '@/types/feature'
import { cn } from '@/utils/classnames'
import MailAndCodeAuth from './components/mail-and-code-auth'

View File

@ -1,10 +1,10 @@
'use client'
import Link from 'next/link'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import { useAppContext } from '@/context/app-context'
import Link from '@/next/link'
import { useSendDeleteAccountEmail } from '../state'
type DeleteAccountProps = {

View File

@ -1,10 +1,10 @@
'use client'
import Link from 'next/link'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Countdown from '@/app/components/signin/countdown'
import Link from '@/next/link'
import { useAccountDeleteStore, useConfirmDeleteAccount, useSendDeleteAccountEmail } from '../state'
const CODE_EXP = /[A-Z\d]{6}/gi

View File

@ -9,7 +9,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock Next.js Link component
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: function MockLink({ children, href, className, title }: { children: React.ReactNode, href: string, className?: string, title?: string }) {
return (
<a href={href} className={className} title={title} data-testid="nav-link">

View File

@ -1,8 +1,8 @@
'use client'
import type { RemixiconComponentType } from '@remixicon/react'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import * as React from 'react'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
export type NavIcon = React.ComponentType<

View File

@ -2,7 +2,6 @@
import type { FC } from 'react'
import type { DataSet } from '@/models/datasets'
import { useInfiniteScroll } from 'ahooks'
import Link from 'next/link'
import * as React from 'react'
import { useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -14,6 +13,7 @@ import Modal from '@/app/components/base/modal'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import FeatureIcon from '@/app/components/header/account-setting/model-provider-page/model-selector/feature-icon'
import { useKnowledge } from '@/hooks/use-knowledge'
import Link from '@/next/link'
import { useInfiniteDatasets } from '@/service/knowledge/use-dataset'
import { cn } from '@/utils/classnames'

View File

@ -1,9 +1,9 @@
'use client'
import type { FC, SVGProps } from 'react'
import type { App } from '@/types/app'
import Link from 'next/link'
import * as React from 'react'
import { Trans, useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { AppModeEnum } from '@/types/app'
import { getRedirectionPath } from '@/utils/app-redirection'
import { basePath } from '@/utils/var'

View File

@ -4,7 +4,6 @@ import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
import type { AppDetailResponse } from '@/models/app'
import type { AppIconType, AppSSO, Language } from '@/types/app'
import { RiArrowRightSLine, RiCloseLine } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
@ -26,6 +25,7 @@ import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/con
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { languages } from '@/i18n-config/language'
import Link from '@/next/link'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'

View File

@ -3,7 +3,6 @@ import type { AppDetailResponse } from '@/models/app'
import type { AppTrigger } from '@/service/use-tools'
import type { AppSSO } from '@/types/app'
import type { I18nKeysByPrefix } from '@/types/i18n'
import Link from 'next/link'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
@ -13,6 +12,7 @@ import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-s
import { BlockEnum } from '@/app/components/workflow/types'
import { useAppContext } from '@/context/app-context'
import { useDocLink } from '@/context/i18n'
import Link from '@/next/link'
import {
useAppTriggers,

View File

@ -53,7 +53,7 @@ vi.mock('next/navigation', () => ({
}),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => <a href={href}>{children}</a>,
}))

View File

@ -1,7 +1,7 @@
import { RiDiscordFill, RiDiscussLine, RiGithubFill } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
type CustomLinkProps = {
href: string

View File

@ -1,6 +1,5 @@
import type { FC, MouseEvent } from 'react'
import type { Resources } from './index'
import Link from 'next/link'
import { Fragment, useState } from 'react'
import { useTranslation } from 'react-i18next'
import FileIcon from '@/app/components/base/file-icon'
@ -9,6 +8,7 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Link from '@/next/link'
import { useDocumentDownload } from '@/service/knowledge/use-document'
import { downloadUrl } from '@/utils/download'
import ProgressTooltip from './progress-tooltip'

View File

@ -1,7 +1,7 @@
import type { I18nKeysWithPrefix } from '@/types/i18n'
import { RiLock2Fill } from '@remixicon/react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
type EncryptedKey = I18nKeysWithPrefix<'common', 'provider.encrypted.'>

View File

@ -4,7 +4,7 @@ import { vi } from 'vitest'
import { AppModeEnum } from '@/types/app'
import LinkedAppsPanel from '../index'
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, className }: { children: React.ReactNode, href: string, className: string }) => (
<a href={href} className={className} data-testid="link-item">
{children}

View File

@ -2,9 +2,9 @@
import type { FC } from 'react'
import type { RelatedApp } from '@/models/datasets'
import { RiArrowRightUpLine } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import AppIcon from '@/app/components/base/app-icon'
import Link from '@/next/link'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'

View File

@ -1,7 +1,7 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
import { fireEvent, render, screen, within } from '@testing-library/react'
import Link from 'next/link'
import { describe, expect, it, vi } from 'vitest'
import Link from '@/next/link'
import {
DropdownMenu,
DropdownMenuContent,
@ -14,7 +14,7 @@ import {
DropdownMenuTrigger,
} from '../index'
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({
href,
children,

View File

@ -3,7 +3,7 @@ import * as React from 'react'
import Footer from '../footer'
import { CategoryEnum } from '../types'
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, className, target }: { children: React.ReactNode, href: string, className?: string, target?: string }) => (
<a href={href} className={className} target={target} data-testid="pricing-link">
{children}

View File

@ -19,7 +19,7 @@ vi.mock('../plans/self-hosted-plan-item/list', () => ({
),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, className, target }: { children: React.ReactNode, href: string, className?: string, target?: string }) => (
<a href={href} className={className} target={target} data-testid="pricing-link">
{children}

View File

@ -1,7 +1,7 @@
import type { Category } from './types'
import Link from 'next/link'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { CategoryEnum } from './types'

View File

@ -1,7 +1,7 @@
import { RiArrowLeftLine } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import Button from '../../base/button'
const Header = () => {

View File

@ -24,7 +24,7 @@ const IndexingTypeValues = {
}
// Mock next/link
vi.mock('next/link', () => {
vi.mock('@/next/link', () => {
return function MockLink({ children, href }: { children: React.ReactNode, href: string }) {
return <a href={href}>{children}</a>
}

View File

@ -6,7 +6,6 @@ import {
RiLoader2Fill,
RiTerminalBoxLine,
} from '@remixicon/react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -15,6 +14,7 @@ import Divider from '@/app/components/base/divider'
import { Plan } from '@/app/components/billing/type'
import { useProviderContext } from '@/context/provider-context'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import Link from '@/next/link'
import { useProcessRule } from '@/service/knowledge/use-dataset'
import { useInvalidDocumentList } from '@/service/knowledge/use-document'
import IndexingProgressItem from './indexing-progress-item'

View File

@ -6,7 +6,7 @@ import { ChunkingMode } from '@/models/datasets'
import { IndexingType } from '../../hooks'
import { IndexingModeSection } from '../indexing-mode-section'
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children?: React.ReactNode, href?: string, className?: string }) => <a href={href} {...props}>{children}</a>,
}))

View File

@ -3,7 +3,6 @@
import type { FC } from 'react'
import type { DefaultModel, Model } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { RetrievalConfig } from '@/types/app'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import Button from '@/app/components/base/button'
@ -16,6 +15,7 @@ import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-me
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
import { useDocLink } from '@/context/i18n'
import { ChunkingMode } from '@/models/datasets'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { indexMethodIcon } from '../../icons'
import { IndexingType } from '../hooks'

View File

@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'
import { TopBar } from '../index'
// Mock next/link to capture href values
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, replace, className }: { children: React.ReactNode, href: string, replace?: boolean, className?: string }) => (
<a href={href} data-replace={replace} className={className} data-testid="back-link">
{children}

View File

@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { StepperProps } from '../stepper'
import { RiArrowLeftLine } from '@remixicon/react'
import Link from 'next/link'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { Stepper } from '../stepper'

View File

@ -101,7 +101,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock next/link
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
<a href={href}>{children}</a>
),

View File

@ -7,7 +7,7 @@ vi.mock('next/navigation', () => ({
useParams: () => ({ datasetId: 'test-ds-id' }),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
<a href={href} data-testid="back-link">{children}</a>
),

View File

@ -9,7 +9,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock next/link to capture href
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, replace }: { children: React.ReactNode, href: string, replace?: boolean }) => (
<a href={href} data-replace={replace}>
{children}

View File

@ -1,11 +1,11 @@
import { RiArrowRightLine } from '@remixicon/react'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Checkbox from '@/app/components/base/checkbox'
import Link from '@/next/link'
type ActionsProps = {
disabled?: boolean

View File

@ -1,10 +1,10 @@
import type { Step } from './step-indicator'
import { RiArrowLeftLine } from '@remixicon/react'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import * as React from 'react'
import Button from '@/app/components/base/button'
import Effect from '@/app/components/base/effect'
import Link from '@/next/link'
import StepIndicator from './step-indicator'
type LeftHeaderProps = {

View File

@ -17,7 +17,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock next/link
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: function MockLink({ children, href, ...props }: { children: React.ReactNode, href: string }) {
return <a href={href} {...props}>{children}</a>
},

View File

@ -10,7 +10,6 @@ import {
RiLoader2Fill,
RiTerminalBoxLine,
} from '@remixicon/react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import * as React from 'react'
import { useEffect, useMemo, useState } from 'react'
@ -26,6 +25,7 @@ import DocumentFileIcon from '@/app/components/datasets/common/document-file-ico
import { useProviderContext } from '@/context/provider-context'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import { DatasourceType } from '@/models/pipeline'
import Link from '@/next/link'
import { useIndexingStatusBatch, useProcessRule } from '@/service/knowledge/use-dataset'
import { useInvalidDocumentList } from '@/service/knowledge/use-document'
import { cn } from '@/utils/classnames'

View File

@ -23,7 +23,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock next/link
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children: React.ReactNode, href: string, [key: string]: unknown }) => (
<a href={href} {...props}>{children}</a>
),

View File

@ -1,5 +1,4 @@
import { RiArrowRightUpLine, RiBookOpenLine } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@ -8,6 +7,7 @@ import Indicator from '@/app/components/header/indicator'
import { useSelector as useAppContextSelector } from '@/context/app-context'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import Link from '@/next/link'
import { useDisableDatasetServiceApi, useEnableDatasetServiceApi } from '@/service/knowledge/use-dataset'
import { cn } from '@/utils/classnames'

View File

@ -19,7 +19,7 @@ vi.mock('next/navigation', () => ({
}))
// Mock next/link
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children: React.ReactNode, href: string, [key: string]: unknown }) => (
<a href={href} {...props}>{children}</a>
),

View File

@ -1,5 +1,4 @@
import { RiBookOpenLine, RiKey2Line } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -9,6 +8,7 @@ import { ApiAggregate } from '@/app/components/base/icons/src/vender/knowledge'
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
import Indicator from '@/app/components/header/indicator'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import Link from '@/next/link'
type CardProps = {
apiBaseUrl: string

View File

@ -1,5 +1,5 @@
import Link from 'next/link'
import * as React from 'react'
import Link from '@/next/link'
type OptionProps = {
Icon: React.ComponentType<{ className?: string }>

View File

@ -1,6 +1,5 @@
'use client'
import { useBoolean } from 'ahooks'
import Link from 'next/link'
import { useSelectedLayoutSegments } from 'next/navigation'
import * as React from 'react'
import { useState } from 'react'
@ -8,6 +7,7 @@ import { useTranslation } from 'react-i18next'
import Confirm from '@/app/components/base/confirm'
import Divider from '@/app/components/base/divider'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Link from '@/next/link'
import { useGetInstalledApps, useUninstallApp, useUpdateAppPinStatus } from '@/service/use-explore'
import { cn } from '@/utils/classnames'
import Toast from '../../base/toast'

View File

@ -52,7 +52,7 @@ vi.mock('@/context/workspace-context-provider', () => ({
WorkspaceProvider: ({ children }: { children?: React.ReactNode }) => children,
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children?: React.ReactNode, href?: string }) => <a href={href}>{children}</a>,
}))

View File

@ -2,15 +2,15 @@
import type { LangGeniusVersionResponse } from '@/models/common'
import { RiCloseLine } from '@remixicon/react'
import dayjs from 'dayjs'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import Modal from '@/app/components/base/modal'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
type IAccountSettingProps = {
langGeniusVersionInfo: LangGeniusVersionResponse
onCancel: () => void

View File

@ -1,7 +1,6 @@
'use client'
import type { MouseEventHandler, ReactNode } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -18,6 +17,7 @@ import { useDocLink } from '@/context/i18n'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { env } from '@/env'
import Link from '@/next/link'
import { useLogout } from '@/service/use-common'
import { cn } from '@/utils/classnames'
import AccountAbout from '../account-about'

View File

@ -1,7 +1,7 @@
'use client'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { useAccountIntegrates } from '@/service/use-common'
import { cn } from '@/utils/classnames'
import s from './index.module.css'

View File

@ -16,7 +16,7 @@ vi.mock('next-themes', () => ({
useTheme: vi.fn(),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
<a href={href} data-testid="mock-link">{children}</a>
),

View File

@ -4,7 +4,6 @@ import {
RiArrowRightUpLine,
} from '@remixicon/react'
import { useTheme } from 'next-themes'
import Link from 'next/link'
import {
memo,
useCallback,
@ -15,6 +14,7 @@ import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import List from '@/app/components/plugins/marketplace/list'
import ProviderCard from '@/app/components/plugins/provider-card'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'
import {

View File

@ -7,7 +7,7 @@ import { useMarketplaceAllPlugins } from '../hooks'
import InstallFromMarketplace from '../install-from-marketplace'
// Mock dependencies
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => <a href={href}>{children}</a>,
}))

View File

@ -3,13 +3,13 @@ import type {
} from './declarations'
import type { Plugin } from '@/app/components/plugins/types'
import { useTheme } from 'next-themes'
import Link from 'next/link'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import List from '@/app/components/plugins/marketplace/list'
import ProviderCard from '@/app/components/plugins/provider-card'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'
import {

View File

@ -1,7 +1,7 @@
import { RiErrorWarningFill } from '@remixicon/react'
import Link from 'next/link'
import Tooltip from '@/app/components/base/tooltip'
import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
import Link from '@/next/link'
import { useInstalledPluginList } from '@/service/use-plugins'
type StatusIndicatorsProps = {

View File

@ -1,7 +1,7 @@
import type { PluginProvider } from '@/models/common'
import { LockClosedIcon } from '@heroicons/react/24/solid'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { usePluginProviders } from '@/service/use-common'
import SerpapiPlugin from './SerpapiPlugin'

View File

@ -4,9 +4,9 @@ import {
RiPlanetFill,
RiPlanetLine,
} from '@remixicon/react'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
type ExploreNavProps = {

View File

@ -1,5 +1,4 @@
'use client'
import Link from 'next/link'
import { useCallback } from 'react'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import WorkplaceSelector from '@/app/components/header/account-dropdown/workplace-selector'
@ -10,6 +9,7 @@ import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { WorkspaceProvider } from '@/context/workspace-context-provider'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Link from '@/next/link'
import { Plan } from '../billing/type'
import AccountDropdown from './account-dropdown'
import AppNav from './app-nav'

View File

@ -1,12 +1,12 @@
'use client'
import type { INavSelectorProps } from './nav-selector'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import * as React from 'react'
import { useState } from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import NavSelector from './nav-selector'

View File

@ -1,11 +1,11 @@
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import { Group } from '@/app/components/base/icons/src/vender/other'
import Indicator from '@/app/components/header/indicator'
import { usePluginTaskStatus } from '@/app/components/plugins/plugin-page/plugin-tasks/hooks'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import DownloadingIcon from './downloading-icon'

View File

@ -4,9 +4,9 @@ import {
RiHammerFill,
RiHammerLine,
} from '@remixicon/react'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
type ToolsNavProps = {

View File

@ -2,7 +2,7 @@ import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import DeprecationNotice from '../deprecation-notice'
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
<a data-testid="link" href={href}>{children}</a>
),

View File

@ -2,10 +2,10 @@ import type { FC } from 'react'
import { useTranslation } from '#i18n'
import { RiAlertFill } from '@remixicon/react'
import { camelCase } from 'es-toolkit/string'
import Link from 'next/link'
import * as React from 'react'
import { useMemo } from 'react'
import { Trans } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
type DeprecationNoticeProps = {

View File

@ -7,7 +7,6 @@ import type { FC } from 'react'
import type { Node } from 'reactflow'
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
import type { NodeOutPutVar } from '@/app/components/workflow/types'
import Link from 'next/link'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import {
@ -16,6 +15,7 @@ import {
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { CollectionType } from '@/app/components/tools/types'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import {
ToolAuthorizationSection,

View File

@ -9,7 +9,6 @@ import {
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
@ -21,6 +20,7 @@ import { useGlobalPublicStore } from '@/context/global-public-context'
import { useDocLink } from '@/context/i18n'
import useDocumentTitle from '@/hooks/use-document-title'
import { usePluginInstallation } from '@/hooks/use-query-params'
import Link from '@/next/link'
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
import { sleep } from '@/utils'
import { cn } from '@/utils/classnames'

View File

@ -82,7 +82,7 @@ vi.mock('next/navigation', () => ({
useRouter: () => ({ push: mockPush }),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: PropsWithChildren<{ href: string }>) => (
<a href={href} {...props}>{children}</a>
),

View File

@ -13,7 +13,7 @@ vi.mock('next/navigation', () => ({
useRouter: () => ({ push: mockPush }),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children: React.ReactNode, href: string }) => (
<a href={href} {...props}>{children}</a>
),

View File

@ -24,7 +24,7 @@ vi.mock('next/navigation', () => ({
useRouter: () => ({ push: mockPush }),
}))
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
<a href={href}>{children}</a>
),

View File

@ -10,7 +10,6 @@ import {
useBoolean,
useKeyPress,
} from 'ahooks'
import Link from 'next/link'
import { useParams, useRouter } from 'next/navigation'
import {
memo,
@ -40,6 +39,7 @@ import { useModalContextSelector } from '@/context/modal-context'
import { useProviderContextSelector } from '@/context/provider-context'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import Link from '@/next/link'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
import { useInvalid } from '@/service/use-base'
import {

View File

@ -1,8 +1,8 @@
'use client'
import { RiArrowRightUpLine } from '@remixicon/react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import useTheme from '@/hooks/use-theme'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { NoToolPlaceholder } from '../../base/icons/src/vender/other'
import { ToolTypeEnum } from '../../workflow/block-selector/types'

View File

@ -6,7 +6,6 @@ import type { BlockEnum, OnSelectBlock } from '../types'
import type { ListRef } from './market-place-plugin/list'
import type { TriggerDefaultValue, TriggerWithProvider } from './types'
import { RiArrowRightUpLine } from '@remixicon/react'
import Link from 'next/link'
import {
useCallback,
useEffect,
@ -19,6 +18,7 @@ import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
import { useFeaturedTriggersRecommendations } from '@/service/use-plugins'
import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers'
import { cn } from '@/utils/classnames'

View File

@ -12,7 +12,6 @@ import type { ToolDefaultValue, ToolValue } from './types'
import type { ListProps, ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
import type { OnSelectBlock } from '@/app/components/workflow/types'
import { RiArrowRightUpLine } from '@remixicon/react'
import Link from 'next/link'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
@ -21,6 +20,7 @@ import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useGetLanguage } from '@/context/i18n'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'

View File

@ -4,7 +4,6 @@ import type { ToolDefaultValue, ToolValue } from './types'
import type { Plugin } from '@/app/components/plugins/types'
import type { Locale } from '@/i18n-config'
import { RiMoreLine } from '@remixicon/react'
import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows'
@ -13,6 +12,7 @@ import Tooltip from '@/app/components/base/tooltip'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import Action from '@/app/components/workflow/block-selector/market-place-plugin/action'
import { useGetLanguage } from '@/context/i18n'
import Link from '@/next/link'
import { isServer } from '@/utils/client'
import { formatNumber } from '@/utils/format'
import { getMarketplaceUrl } from '@/utils/var'

View File

@ -3,7 +3,6 @@ import type { TriggerDefaultValue, TriggerWithProvider } from './types'
import type { Plugin } from '@/app/components/plugins/types'
import type { Locale } from '@/i18n-config'
import { RiMoreLine } from '@remixicon/react'
import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows'
@ -12,6 +11,7 @@ import Tooltip from '@/app/components/base/tooltip'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import Action from '@/app/components/workflow/block-selector/market-place-plugin/action'
import { useGetLanguage } from '@/context/i18n'
import Link from '@/next/link'
import { isServer } from '@/utils/client'
import { formatNumber } from '@/utils/format'
import { getMarketplaceUrl } from '@/utils/var'

View File

@ -3,9 +3,9 @@ import type { RefObject } from 'react'
import type { Plugin, PluginCategoryEnum } from '@/app/components/plugins/types'
import { RiArrowRightUpLine, RiSearchLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { cn } from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'
import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll'

View File

@ -3,13 +3,13 @@ import type { Dispatch, SetStateAction } from 'react'
import type { ViewType } from '@/app/components/workflow/block-selector/view-type-select'
import type { OnSelectBlock } from '@/app/components/workflow/types'
import { RiMoreLine } from '@remixicon/react'
import Link from 'next/link'
import * as React from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/arrows'
import Loading from '@/app/components/base/loading'
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
import Link from '@/next/link'
import { useRAGRecommendedPlugins } from '@/service/use-tools'
import { isServer } from '@/utils/client'
import { getMarketplaceUrl } from '@/utils/var'

View File

@ -4,7 +4,6 @@ import type { Strategy } from './agent-strategy'
import type { StrategyPluginDetail } from '@/app/components/plugins/types'
import type { ListProps, ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
import { RiArrowDownSLine, RiErrorWarningFill } from '@remixicon/react'
import Link from 'next/link'
import { memo, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
@ -17,6 +16,7 @@ import { PluginCategoryEnum } from '@/app/components/plugins/types'
import { CollectionType } from '@/app/components/tools/types'
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
import { useStrategyProviders } from '@/service/use-strategy'
import { cn } from '@/utils/classnames'
import Tools from '../../../block-selector/tools'

View File

@ -5,7 +5,6 @@ import type { ToolVarInputs } from '../../tool/types'
import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaTextInput } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { PluginMeta } from '@/app/components/plugins/types'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
@ -26,6 +25,7 @@ import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/m
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
import { useDocLink } from '@/context/i18n'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import Link from '@/next/link'
import { AppModeEnum } from '@/types/app'
import { useWorkflowStore } from '../../../store'
import { AgentStrategySelector } from './agent-strategy-selector'

View File

@ -3,7 +3,6 @@
import type { FC, ReactNode } from 'react'
import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import Link from 'next/link'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
@ -13,6 +12,7 @@ import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-ico
import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model'
import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
import Link from '@/next/link'
import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
import { cn } from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'

View File

@ -1,6 +1,5 @@
'use client'
import { RiExternalLinkLine } from '@remixicon/react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
@ -9,6 +8,7 @@ import Modal from '@/app/components/base/modal'
import { useDocLink } from '@/context/i18n'
import { useModalContextSelector } from '@/context/modal-context'
import useTimestamp from '@/hooks/use-timestamp'
import Link from '@/next/link'
import { useEducationVerify } from '@/service/use-education'
import { SparklesSoftAccent } from '../components/base/icons/src/public/common'

View File

@ -1,9 +1,8 @@
'use client'
import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common'
import { useStore } from '@tanstack/react-form'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import * as React from 'react'
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
@ -13,9 +12,10 @@ import { formContext, useAppForm } from '@/app/components/base/form'
import { zodSubmitValidator } from '@/app/components/base/form/utils/zod-submit-validator'
import Input from '@/app/components/base/input'
import { validPassword } from '@/config'
import { LICENSE_LINK } from '@/constants/link'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
import { fetchInitValidateStatus, fetchSetupStatus, login, setup } from '@/service/common'
import { cn } from '@/utils/classnames'
import { encryptPassword as encodePassword } from '@/utils/encryption'

View File

@ -1,5 +1,5 @@
import Link from 'next/link'
import Loading from '@/app/components/base/loading'
import Link from '@/next/link'
const Home = async () => {
return (

View File

@ -1,7 +1,6 @@
'use client'
import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -11,6 +10,7 @@ import Toast from '@/app/components/base/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
import { sendResetPasswordCode } from '@/service/common'
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../components/signin/countdown'

View File

@ -1,6 +1,5 @@
import type { ResponseError } from '@/service/fetch'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -10,6 +9,7 @@ import Input from '@/app/components/base/input'
import Toast from '@/app/components/base/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import Link from '@/next/link'
import { login } from '@/service/common'
import { setWebAppAccessToken } from '@/service/webapp-auth'
import { encryptPassword } from '@/utils/encryption'

View File

@ -2,7 +2,6 @@
import type { Locale } from '@/i18n-config'
import { RiAccountCircleLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -15,6 +14,7 @@ import { LICENSE_LINK } from '@/constants/link'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { setLocaleOnClient } from '@/i18n-config'
import { languages, LanguagesSupported } from '@/i18n-config/language'
import Link from '@/next/link'
import { activateMember } from '@/service/common'
import { useInvitationCheck } from '@/service/use-common'
import { timezones } from '@/utils/timezone'

View File

@ -1,5 +1,4 @@
import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
@ -7,6 +6,7 @@ import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
import { invitationCheck } from '@/service/common'
import { useIsLogin } from '@/service/use-common'
import { LicenseStatus } from '@/types/feature'

View File

@ -1,6 +1,5 @@
'use client'
import type { Reducer } from 'react'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { useReducer } from 'react'
import { useTranslation } from 'react-i18next'
@ -10,6 +9,7 @@ import Toast from '@/app/components/base/toast'
import Tooltip from '@/app/components/base/tooltip'
import { LICENSE_LINK } from '@/constants/link'
import { languages, LanguagesSupported } from '@/i18n-config/language'
import Link from '@/next/link'
import { useOneMoreStep } from '@/service/use-common'
import { timezones } from '@/utils/timezone'
import Input from '../components/base/input'

View File

@ -24,7 +24,7 @@ const buildSystemFeatures = (overrides: SystemFeaturesOverrides = {}): SystemFea
},
})
vi.mock('next/link', () => ({
vi.mock('@/next/link', () => ({
default: ({ children, href, className, target, rel }: { children: React.ReactNode, href: string, className?: string, target?: string, rel?: string }) => (
<a href={href} className={className} target={target} rel={rel}>
{children}

View File

@ -1,6 +1,5 @@
'use client'
import type { MailSendResponse } from '@/service/use-common'
import Link from 'next/link'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
@ -10,6 +9,7 @@ import Split from '@/app/signin/split'
import { emailRegex } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useLocale } from '@/context/i18n'
import Link from '@/next/link'
import { useSendMail } from '@/service/use-common'
type Props = {

View File

@ -46,6 +46,10 @@ const NEXT_PLATFORM_RESTRICTED_IMPORT_PATTERNS = [
group: ['next/server'],
message: 'Import Next APIs from @/next/server instead of next/server.',
},
{
group: ['next/link'],
message: 'Import Next APIs from @/next/link instead of next/link.',
},
]
const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [

1
web/next/link.ts Normal file
View File

@ -0,0 +1 @@
export { default } from 'next/link'