mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 14:14:17 +08:00
lint and typecheck for doc link
This commit is contained in:
parent
9161936f41
commit
46f0e3c07e
@ -1,4 +1,5 @@
|
||||
import type { Locale } from '@/i18n-config/language'
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { useTranslation } from '#i18n'
|
||||
import { getDocLanguage, getLanguage, getPricingPageLanguage } from '@/i18n-config/language'
|
||||
|
||||
@ -19,12 +20,14 @@ export const useGetPricingPageLanguage = () => {
|
||||
}
|
||||
|
||||
export const defaultDocBaseUrl = 'https://docs.dify.ai'
|
||||
export const useDocLink = (baseUrl?: string): ((path?: string, pathMap?: { [index: string]: string }) => string) => {
|
||||
export type DocPathMap = Partial<Record<Locale, DocPathWithoutLang>>
|
||||
|
||||
export const useDocLink = (baseUrl?: string): ((path?: DocPathWithoutLang, pathMap?: DocPathMap) => string) => {
|
||||
let baseDocUrl = baseUrl || defaultDocBaseUrl
|
||||
baseDocUrl = (baseDocUrl.endsWith('/')) ? baseDocUrl.slice(0, -1) : baseDocUrl
|
||||
const locale = useLocale()
|
||||
const docLanguage = getDocLanguage(locale)
|
||||
return (path?: string, pathMap?: { [index: string]: string }): string => {
|
||||
return (path?: DocPathWithoutLang, pathMap?: DocPathMap): string => {
|
||||
const pathUrl = path || ''
|
||||
let targetPath = (pathMap) ? pathMap[locale] || pathUrl : pathUrl
|
||||
targetPath = (targetPath.startsWith('/')) ? targetPath.slice(1) : targetPath
|
||||
|
||||
70
web/eslint-rules/doc-redirects.js
Normal file
70
web/eslint-rules/doc-redirects.js
Normal file
@ -0,0 +1,70 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
//
|
||||
// Generated from: https://raw.githubusercontent.com/langgenius/dify-docs/refs/heads/main/docs.json
|
||||
// Generated at: 2026-01-12T06:51:06.502Z
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
export const docRedirects = new Map([
|
||||
['guides/knowledge-base/retrieval-test-and-citation', 'use-dify/knowledge/test-retrieval'],
|
||||
['use-dify/knowledge/retrieval-test-and-citation', 'use-dify/knowledge/test-retrieval'],
|
||||
['use-dify/build/variables#conversation-variables', 'use-dify/getting-started/key-concepts#variables'],
|
||||
['use-dify/build/variables#会话变量', 'use-dify/getting-started/key-concepts#变量'],
|
||||
['use-dify/build/variables#会話変数', 'use-dify/getting-started/key-concepts#変数'],
|
||||
['guides/workflow/variables#conversation-variables', 'use-dify/getting-started/key-concepts#variables'],
|
||||
['guides/workflow/variables#会话变量', 'use-dify/getting-started/key-concepts#变量'],
|
||||
['guides/workflow/variables#会話変数', 'use-dify/getting-started/key-concepts#変数'],
|
||||
['guides/workflow/node/start', 'use-dify/getting-started/key-concepts#workflow'],
|
||||
['guides/workflow/node/user-input', 'use-dify/nodes/user-input'],
|
||||
['guides/workflow/node/trigger', 'use-dify/nodes/trigger/overview'],
|
||||
['guides/workflow/node/schedule-trigger', 'use-dify/nodes/trigger/schedule-trigger'],
|
||||
['guides/workflow/node/webhook-trigger', 'use-dify/nodes/trigger/webhook-trigger'],
|
||||
['guides/workflow/node/plugin-trigger', 'use-dify/nodes/trigger/plugin-trigger'],
|
||||
['guides/workflow/node/knowledge-retrieval', 'use-dify/nodes/knowledge-retrieval'],
|
||||
['guides/workflow/node/agent', 'use-dify/nodes/agent'],
|
||||
['guides/workflow/node/question-classifier', 'use-dify/nodes/question-classifier'],
|
||||
['guides/workflow/node/ifelse', 'use-dify/nodes/ifelse'],
|
||||
['guides/workflow/node/iteration', 'use-dify/nodes/iteration'],
|
||||
['guides/workflow/node/loop', 'use-dify/nodes/loop'],
|
||||
['guides/workflow/node/code', 'use-dify/nodes/code'],
|
||||
['guides/workflow/node/template', 'use-dify/nodes/template'],
|
||||
['guides/workflow/node/variable-aggregator', 'use-dify/nodes/variable-aggregator'],
|
||||
['guides/workflow/node/doc-extractor', 'use-dify/nodes/doc-extractor'],
|
||||
['guides/workflow/node/variable-assigner', 'use-dify/nodes/variable-assigner'],
|
||||
['guides/workflow/node/parameter-extractor', 'use-dify/nodes/parameter-extractor'],
|
||||
['guides/workflow/node/http-request', 'use-dify/nodes/http-request'],
|
||||
['guides/workflow/node/list-operator', 'use-dify/nodes/list-operator'],
|
||||
['guides/workflow/node/tools', 'use-dify/nodes/tools'],
|
||||
['guides/workflow/node/end', 'use-dify/nodes/output'],
|
||||
['guides/workflow/node/llm', 'use-dify/nodes/llm'],
|
||||
['getting-started/dify-for-education', 'use-dify/workspace/subscription-management#dify-for-education'],
|
||||
['/plugins/schema-definition/model', '/versions/legacy/en/plugins/schema-definition/model/model-designing-rules'],
|
||||
['/plugins/schema-definition', '/versions/legacy/en/plugins/schema-definition/manifest'],
|
||||
['/zh-hans', 'use-dify/getting-started/introduction'],
|
||||
['/introduction', 'use-dify/getting-started/introduction'],
|
||||
['introduction', 'use-dify/getting-started/introduction'],
|
||||
['/getting-started/readme/features-and-specifications', 'use-dify/getting-started/introduction'],
|
||||
['/getting-started/readme/model-providers', 'use-dify/getting-started/introduction'],
|
||||
['/getting-started/install-self-hosted', 'self-host/quick-start/docker-compose'],
|
||||
['/getting-started/install-self-hosted/docker-compose', 'self-host/quick-start/docker-compose'],
|
||||
['/getting-started/install-self-hosted/local-source-code', 'self-host/advanced-deployments/local-source-code'],
|
||||
['/getting-started/install-self-hosted/bt-panel', 'self-host/platform-guides/bt-panel'],
|
||||
['/getting-started/install-self-hosted/start-the-frontend-docker-container', 'self-host/advanced-deployments/start-the-frontend-docker-container'],
|
||||
['/getting-started/install-self-hosted/environments', 'self-host/configuration/environments'],
|
||||
['/getting-started/install-self-hosted/faqs', 'self-host/quick-start/faqs'],
|
||||
['/getting-started/cloud', 'use-dify/getting-started/introduction'],
|
||||
['/getting-started/dify-premium', 'self-host/platform-guides/dify-premium'],
|
||||
['guides/application-publishing/launch-your-webapp-quickly/readme', 'use-dify/publish/README'],
|
||||
['/plugins/quick-start', 'develop-plugin/getting-started/getting-started-dify-plugin'],
|
||||
['/plugins/quick-start/install-plugins', 'develop-plugin/getting-started/getting-started-dify-plugin'],
|
||||
['/plugins/quick-start/develop-plugins', 'develop-plugin/dev-guides-and-walkthroughs/cheatsheet'],
|
||||
['/plugins/quick-start/develop-plugins/initialize-development-tools', 'develop-plugin/getting-started/cli'],
|
||||
['/plugins/quick-start/develop-plugins/tool-plugin', 'develop-plugin/features-and-specs/plugin-types/tool'],
|
||||
['/plugins/quick-start/develop-plugins/model-plugin', 'develop-plugin/features-and-specs/plugin-types/model-schema'],
|
||||
['/plugins/quick-start/develop-plugins/model-plugin/create-model-providers', 'develop-plugin/dev-guides-and-walkthroughs/creating-new-model-provider'],
|
||||
['learn-more/extended-reading/dify-docs-mcp', 'use-dify/build/mcp'],
|
||||
['getting-started/cloud', 'use-dify/getting-started/introduction'],
|
||||
['getting-started/dify-premium', 'use-dify/getting-started/introduction'],
|
||||
['guides/model-configuration/load-balancing', 'use-dify/workspace/model-providers#configure-model-load-balancing'],
|
||||
['/guides/model-configuration/load-balancing', 'use-dify/workspace/model-providers#configure-model-load-balancing'],
|
||||
])
|
||||
@ -1,4 +1,5 @@
|
||||
import noAsAnyInT from './rules/no-as-any-in-t.js'
|
||||
import noDeprecatedDocLink from './rules/no-deprecated-doc-link.js'
|
||||
import noExtraKeys from './rules/no-extra-keys.js'
|
||||
import noLegacyNamespacePrefix from './rules/no-legacy-namespace-prefix.js'
|
||||
import requireNsOption from './rules/require-ns-option.js'
|
||||
@ -12,6 +13,7 @@ const plugin = {
|
||||
},
|
||||
rules: {
|
||||
'no-as-any-in-t': noAsAnyInT,
|
||||
'no-deprecated-doc-link': noDeprecatedDocLink,
|
||||
'no-extra-keys': noExtraKeys,
|
||||
'no-legacy-namespace-prefix': noLegacyNamespacePrefix,
|
||||
'require-ns-option': requireNsOption,
|
||||
|
||||
172
web/eslint-rules/rules/no-deprecated-doc-link.js
Normal file
172
web/eslint-rules/rules/no-deprecated-doc-link.js
Normal file
@ -0,0 +1,172 @@
|
||||
import { docRedirects } from '../doc-redirects.js'
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
export default {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Disallow deprecated documentation paths in useDocLink calls and auto-fix to new paths',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
messages: {
|
||||
deprecatedDocLink:
|
||||
'Deprecated documentation path detected. The path "{{oldPath}}" has been moved to "{{newPath}}".',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
/**
|
||||
* Check if the path needs redirect
|
||||
* @param {string} docPath - Path without language prefix
|
||||
*/
|
||||
function checkRedirect(docPath) {
|
||||
// Normalize path (remove leading slash if present)
|
||||
const normalizedPath = docPath.startsWith('/') ? docPath.slice(1) : docPath
|
||||
|
||||
if (docRedirects.has(normalizedPath)) {
|
||||
return {
|
||||
oldPath: normalizedPath,
|
||||
newPath: docRedirects.get(normalizedPath),
|
||||
}
|
||||
}
|
||||
|
||||
// Also check with leading slash
|
||||
if (docRedirects.has(`/${normalizedPath}`)) {
|
||||
return {
|
||||
oldPath: normalizedPath,
|
||||
newPath: docRedirects.get(`/${normalizedPath}`).replace(/^\//, ''),
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if node is a useDocLink call
|
||||
* @param {import('estree').CallExpression} node
|
||||
*/
|
||||
function isUseDocLinkCall(node) {
|
||||
// Check for direct useDocLink() call that returns a function
|
||||
// The actual doc path is passed to the returned function
|
||||
// e.g., const getLink = useDocLink(); getLink('path')
|
||||
// or useDocLink()('path')
|
||||
|
||||
// For the pattern: useDocLink()('path')
|
||||
if (
|
||||
node.callee.type === 'CallExpression'
|
||||
&& node.callee.callee.type === 'Identifier'
|
||||
&& node.callee.callee.name === 'useDocLink'
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if node is a call to a function returned by useDocLink
|
||||
* This handles: const docLink = useDocLink(); docLink('path')
|
||||
*/
|
||||
function isDocLinkFunctionCall(node, scope) {
|
||||
if (node.callee.type !== 'Identifier')
|
||||
return false
|
||||
|
||||
const calleeName = node.callee.name
|
||||
|
||||
// Look for variable declaration that assigns useDocLink()
|
||||
const variable = scope.variables.find(v => v.name === calleeName)
|
||||
if (!variable || variable.defs.length === 0)
|
||||
return false
|
||||
|
||||
const def = variable.defs[0]
|
||||
if (def.type !== 'Variable' || !def.node.init)
|
||||
return false
|
||||
|
||||
const init = def.node.init
|
||||
|
||||
// Check if initialized with useDocLink()
|
||||
if (
|
||||
init.type === 'CallExpression'
|
||||
&& init.callee.type === 'Identifier'
|
||||
&& init.callee.name === 'useDocLink'
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a deprecated path and provide fix
|
||||
*/
|
||||
function reportDeprecatedPath(node, redirect, sourceCode) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'deprecatedDocLink',
|
||||
data: {
|
||||
oldPath: redirect.oldPath,
|
||||
newPath: redirect.newPath,
|
||||
},
|
||||
fix(fixer) {
|
||||
// Replace the string value, preserving the quote style
|
||||
const raw = sourceCode.getText(node)
|
||||
const quote = raw[0]
|
||||
return fixer.replaceText(node, `${quote}${redirect.newPath}${quote}`)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a string literal node for deprecated path
|
||||
*/
|
||||
function checkStringLiteral(node, sourceCode) {
|
||||
if (node.type !== 'Literal' || typeof node.value !== 'string')
|
||||
return
|
||||
|
||||
const redirect = checkRedirect(node.value)
|
||||
if (redirect)
|
||||
reportDeprecatedPath(node, redirect, sourceCode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check pathMap object for deprecated paths
|
||||
* pathMap is like { en: 'path1', zh: 'path2' }
|
||||
*/
|
||||
function checkPathMapObject(node, sourceCode) {
|
||||
if (node.type !== 'ObjectExpression')
|
||||
return
|
||||
|
||||
for (const prop of node.properties) {
|
||||
if (prop.type !== 'Property')
|
||||
continue
|
||||
|
||||
// Check the value of each property
|
||||
checkStringLiteral(prop.value, sourceCode)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const sourceCode = context.sourceCode || context.getSourceCode()
|
||||
const scope = sourceCode.getScope?.(node) || context.getScope()
|
||||
|
||||
// Check if this is useDocLink()('path') pattern
|
||||
const isDirectCall = isUseDocLinkCall(node)
|
||||
|
||||
// Check if this is docLink('path') where docLink = useDocLink()
|
||||
const isIndirectCall = isDocLinkFunctionCall(node, scope)
|
||||
|
||||
if (!isDirectCall && !isIndirectCall)
|
||||
return
|
||||
|
||||
// Check first argument (the doc path)
|
||||
if (node.arguments.length >= 1)
|
||||
checkStringLiteral(node.arguments[0], sourceCode)
|
||||
|
||||
// Check second argument (pathMap object)
|
||||
if (node.arguments.length >= 2)
|
||||
checkPathMapObject(node.arguments[1], sourceCode)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -178,6 +178,7 @@ export default antfu(
|
||||
rules: {
|
||||
// 'dify-i18n/no-as-any-in-t': ['error', { mode: 'all' }],
|
||||
'dify-i18n/no-as-any-in-t': 'error',
|
||||
'dify-i18n/no-deprecated-doc-link': 'error',
|
||||
// 'dify-i18n/no-legacy-namespace-prefix': 'error',
|
||||
// 'dify-i18n/require-ns-option': 'error',
|
||||
},
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { DocLanguage } from '@/types/doc-paths'
|
||||
import data from './languages'
|
||||
|
||||
export type Item = {
|
||||
@ -22,9 +23,9 @@ export const getLanguage = (locale: Locale): Locale => {
|
||||
return LanguagesSupported[0].replace('-', '_') as Locale
|
||||
}
|
||||
|
||||
const DOC_LANGUAGE: Record<string, string> = {
|
||||
'zh-Hans': 'zh-hans',
|
||||
'ja-JP': 'ja-jp',
|
||||
const DOC_LANGUAGE: Record<string, DocLanguage | undefined> = {
|
||||
'zh-Hans': 'zh',
|
||||
'ja-JP': 'ja',
|
||||
'en-US': 'en',
|
||||
}
|
||||
|
||||
@ -56,7 +57,7 @@ export const localeMap: Record<Locale, string> = {
|
||||
'ar-TN': 'ar',
|
||||
}
|
||||
|
||||
export const getDocLanguage = (locale: string) => {
|
||||
export const getDocLanguage = (locale: string): DocLanguage => {
|
||||
return DOC_LANGUAGE[locale] || 'en'
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
"type-check:tsgo": "tsgo --noEmit",
|
||||
"prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky",
|
||||
"gen-icons": "node ./scripts/gen-icons.mjs && eslint --fix app/components/base/icons/src/",
|
||||
"gen-doc-paths": "tsx ./scripts/gen-doc-paths.ts",
|
||||
"uglify-embed": "node ./bin/uglify-embed",
|
||||
"i18n:check": "tsx ./scripts/check-i18n.js",
|
||||
"test": "vitest run",
|
||||
|
||||
271
web/scripts/gen-doc-paths.ts
Normal file
271
web/scripts/gen-doc-paths.ts
Normal file
@ -0,0 +1,271 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
//
|
||||
// This script fetches the docs.json from dify-docs repository
|
||||
// and generates TypeScript types for documentation paths.
|
||||
//
|
||||
// Usage: pnpm gen-doc-paths
|
||||
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const DOCS_JSON_URL = 'https://raw.githubusercontent.com/langgenius/dify-docs/refs/heads/main/docs.json'
|
||||
const OUTPUT_PATH = path.resolve(__dirname, '../types/doc-paths.ts')
|
||||
const REDIRECTS_PATH = path.resolve(__dirname, '../eslint-rules/doc-redirects.js')
|
||||
|
||||
type NavItem = string | NavObject | NavItem[]
|
||||
|
||||
type NavObject = {
|
||||
pages?: NavItem[]
|
||||
groups?: NavItem[]
|
||||
dropdowns?: NavItem[]
|
||||
languages?: NavItem[]
|
||||
versions?: NavItem[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
type Redirect = {
|
||||
source: string
|
||||
destination: string
|
||||
}
|
||||
|
||||
type DocsJson = {
|
||||
navigation?: NavItem
|
||||
redirects?: Redirect[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively extract all page paths from navigation structure
|
||||
*/
|
||||
function extractPaths(item: NavItem | undefined, paths: Set<string> = new Set()): Set<string> {
|
||||
if (!item)
|
||||
return paths
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
for (const el of item)
|
||||
extractPaths(el, paths)
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
if (typeof item === 'string') {
|
||||
paths.add(item)
|
||||
return paths
|
||||
}
|
||||
|
||||
if (typeof item === 'object') {
|
||||
// Handle pages array
|
||||
if (item.pages)
|
||||
extractPaths(item.pages, paths)
|
||||
|
||||
// Handle groups array
|
||||
if (item.groups)
|
||||
extractPaths(item.groups, paths)
|
||||
|
||||
// Handle dropdowns
|
||||
if (item.dropdowns)
|
||||
extractPaths(item.dropdowns, paths)
|
||||
|
||||
// Handle languages
|
||||
if (item.languages)
|
||||
extractPaths(item.languages, paths)
|
||||
|
||||
// Handle versions in navigation
|
||||
if (item.versions)
|
||||
extractPaths(item.versions, paths)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
/**
|
||||
* Group paths by their prefix structure
|
||||
*/
|
||||
function groupPathsBySection(paths: Set<string>): Record<string, Set<string>> {
|
||||
const groups: Record<string, Set<string>> = {}
|
||||
|
||||
for (const fullPath of paths) {
|
||||
// Skip non-doc paths (like .json files for OpenAPI)
|
||||
if (fullPath.endsWith('.json'))
|
||||
continue
|
||||
|
||||
// Remove language prefix (en/, zh/, ja/)
|
||||
const withoutLang = fullPath.replace(/^(en|zh|ja)\//, '')
|
||||
if (!withoutLang || withoutLang === fullPath)
|
||||
continue
|
||||
|
||||
// Get section (first part of path)
|
||||
const parts = withoutLang.split('/')
|
||||
const section = parts[0]
|
||||
|
||||
if (!groups[section])
|
||||
groups[section] = new Set()
|
||||
|
||||
groups[section].add(withoutLang)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert section name to TypeScript type name
|
||||
*/
|
||||
function sectionToTypeName(section: string): string {
|
||||
return section
|
||||
.split('-')
|
||||
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate TypeScript type definitions
|
||||
*/
|
||||
function generateTypeDefinitions(groups: Record<string, Set<string>>): string {
|
||||
const lines: string[] = [
|
||||
'// GENERATE BY script',
|
||||
'// DON NOT EDIT IT MANUALLY',
|
||||
'//',
|
||||
'// Generated from: https://raw.githubusercontent.com/langgenius/dify-docs/refs/heads/main/docs.json',
|
||||
`// Generated at: ${new Date().toISOString()}`,
|
||||
'',
|
||||
'// Language prefixes',
|
||||
'export type DocLanguage = \'en\' | \'zh\' | \'ja\'',
|
||||
'',
|
||||
]
|
||||
|
||||
const typeNames: string[] = []
|
||||
|
||||
// Generate type for each section
|
||||
for (const [section, pathsSet] of Object.entries(groups)) {
|
||||
const paths = Array.from(pathsSet).sort()
|
||||
const typeName = `${sectionToTypeName(section)}Path`
|
||||
typeNames.push(typeName)
|
||||
|
||||
lines.push(`// ${sectionToTypeName(section)} paths`)
|
||||
lines.push(`export type ${typeName} =`)
|
||||
|
||||
for (const p of paths) {
|
||||
lines.push(` | '${p}'`)
|
||||
}
|
||||
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
// Generate API reference type (for .json files)
|
||||
lines.push('// API Reference paths')
|
||||
lines.push('export type ApiReferencePath =')
|
||||
lines.push(' | \'api-reference/openapi_chat.json\'')
|
||||
lines.push(' | \'api-reference/openapi_chatflow.json\'')
|
||||
lines.push(' | \'api-reference/openapi_workflow.json\'')
|
||||
lines.push(' | \'api-reference/openapi_knowledge.json\'')
|
||||
lines.push(' | \'api-reference/openapi_completion.json\'')
|
||||
lines.push('')
|
||||
typeNames.push('ApiReferencePath')
|
||||
|
||||
// Generate combined type
|
||||
lines.push('// Combined path without language prefix')
|
||||
lines.push('export type DocPathWithoutLang =')
|
||||
for (const typeName of typeNames) {
|
||||
lines.push(` | ${typeName}`)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
// Generate full path type with language prefix
|
||||
lines.push('// Full documentation path with language prefix')
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
lines.push('export type DifyDocPath = `${DocLanguage}/${DocPathWithoutLang}`')
|
||||
lines.push('')
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate redirects map for ESLint rule
|
||||
* Strips language prefix from paths for use with useDocLink
|
||||
*/
|
||||
function generateRedirectsModule(redirects: Redirect[]): string {
|
||||
const lines: string[] = [
|
||||
'// GENERATE BY script',
|
||||
'// DON NOT EDIT IT MANUALLY',
|
||||
'//',
|
||||
'// Generated from: https://raw.githubusercontent.com/langgenius/dify-docs/refs/heads/main/docs.json',
|
||||
`// Generated at: ${new Date().toISOString()}`,
|
||||
'',
|
||||
'/** @type {Map<string, string>} */',
|
||||
'export const docRedirects = new Map([',
|
||||
]
|
||||
|
||||
// Use a map to deduplicate paths (same path in different languages)
|
||||
const pathMap = new Map<string, string>()
|
||||
const langPrefixRegex = /^\/(en|zh|ja|zh-hans|ja-jp)\//
|
||||
|
||||
for (const redirect of redirects) {
|
||||
// Skip wildcard redirects
|
||||
if (redirect.source.includes(':slug'))
|
||||
continue
|
||||
|
||||
// Strip language prefix from source and destination
|
||||
const sourceWithoutLang = redirect.source.replace(langPrefixRegex, '')
|
||||
const destWithoutLang = redirect.destination.replace(langPrefixRegex, '')
|
||||
|
||||
// Only add if we haven't seen this path yet
|
||||
if (!pathMap.has(sourceWithoutLang))
|
||||
pathMap.set(sourceWithoutLang, destWithoutLang)
|
||||
}
|
||||
|
||||
for (const [source, dest] of pathMap) {
|
||||
lines.push(` ['${source}', '${dest}'],`)
|
||||
}
|
||||
|
||||
lines.push('])')
|
||||
lines.push('')
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Fetching docs.json from GitHub...')
|
||||
|
||||
const response = await fetch(DOCS_JSON_URL)
|
||||
if (!response.ok)
|
||||
throw new Error(`Failed to fetch docs.json: ${response.status} ${response.statusText}`)
|
||||
|
||||
const docsJson = await response.json() as DocsJson
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Successfully fetched docs.json')
|
||||
|
||||
// Extract paths from navigation
|
||||
const allPaths = extractPaths(docsJson.navigation)
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Found ${allPaths.size} total paths`)
|
||||
|
||||
// Group by section
|
||||
const groups = groupPathsBySection(allPaths)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Grouped into ${Object.keys(groups).length} sections:`, Object.keys(groups))
|
||||
|
||||
// Generate TypeScript
|
||||
const tsContent = generateTypeDefinitions(groups)
|
||||
|
||||
// Write to file
|
||||
await writeFile(OUTPUT_PATH, tsContent, 'utf-8')
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Generated TypeScript types at: ${OUTPUT_PATH}`)
|
||||
|
||||
// Generate redirects module for ESLint rule
|
||||
const redirects = docsJson.redirects || []
|
||||
const redirectsContent = generateRedirectsModule(redirects)
|
||||
await writeFile(REDIRECTS_PATH, redirectsContent, 'utf-8')
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Generated redirects module at: ${REDIRECTS_PATH} (${redirects.length} redirects)`)
|
||||
}
|
||||
|
||||
main().catch((err: Error) => {
|
||||
console.error('Error:', err.message)
|
||||
process.exit(1)
|
||||
})
|
||||
176
web/types/doc-paths.ts
Normal file
176
web/types/doc-paths.ts
Normal file
@ -0,0 +1,176 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
//
|
||||
// Generated from: https://raw.githubusercontent.com/langgenius/dify-docs/refs/heads/main/docs.json
|
||||
// Generated at: 2026-01-12T06:51:06.500Z
|
||||
|
||||
// Language prefixes
|
||||
export type DocLanguage = 'en' | 'zh' | 'ja'
|
||||
|
||||
// UseDify paths
|
||||
export type UseDifyPath =
|
||||
| 'use-dify/build/additional-features'
|
||||
| 'use-dify/build/goto-anything'
|
||||
| 'use-dify/build/mcp'
|
||||
| 'use-dify/build/orchestrate-node'
|
||||
| 'use-dify/build/predefined-error-handling-logic'
|
||||
| 'use-dify/build/shortcut-key'
|
||||
| 'use-dify/build/version-control'
|
||||
| 'use-dify/debug/error-type'
|
||||
| 'use-dify/debug/history-and-logs'
|
||||
| 'use-dify/debug/step-run'
|
||||
| 'use-dify/debug/variable-inspect'
|
||||
| 'use-dify/getting-started/introduction'
|
||||
| 'use-dify/getting-started/key-concepts'
|
||||
| 'use-dify/getting-started/quick-start'
|
||||
| 'use-dify/knowledge/connect-external-knowledge-base'
|
||||
| 'use-dify/knowledge/create-knowledge/chunking-and-cleaning-text'
|
||||
| 'use-dify/knowledge/create-knowledge/import-text-data/readme'
|
||||
| 'use-dify/knowledge/create-knowledge/import-text-data/sync-from-notion'
|
||||
| 'use-dify/knowledge/create-knowledge/import-text-data/sync-from-website'
|
||||
| 'use-dify/knowledge/create-knowledge/introduction'
|
||||
| 'use-dify/knowledge/create-knowledge/setting-indexing-methods'
|
||||
| 'use-dify/knowledge/external-knowledge-api'
|
||||
| 'use-dify/knowledge/integrate-knowledge-within-application'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/authorize-data-source'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/create-knowledge-pipeline'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/knowledge-pipeline-orchestration'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/manage-knowledge-base'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/publish-knowledge-pipeline'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/readme'
|
||||
| 'use-dify/knowledge/knowledge-pipeline/upload-files'
|
||||
| 'use-dify/knowledge/knowledge-request-rate-limit'
|
||||
| 'use-dify/knowledge/manage-knowledge/introduction'
|
||||
| 'use-dify/knowledge/manage-knowledge/maintain-dataset-via-api'
|
||||
| 'use-dify/knowledge/manage-knowledge/maintain-knowledge-documents'
|
||||
| 'use-dify/knowledge/metadata'
|
||||
| 'use-dify/knowledge/readme'
|
||||
| 'use-dify/knowledge/test-retrieval'
|
||||
| 'use-dify/monitor/analysis'
|
||||
| 'use-dify/monitor/annotation-reply'
|
||||
| 'use-dify/monitor/integrations/integrate-aliyun'
|
||||
| 'use-dify/monitor/integrations/integrate-arize'
|
||||
| 'use-dify/monitor/integrations/integrate-langfuse'
|
||||
| 'use-dify/monitor/integrations/integrate-langsmith'
|
||||
| 'use-dify/monitor/integrations/integrate-opik'
|
||||
| 'use-dify/monitor/integrations/integrate-phoenix'
|
||||
| 'use-dify/monitor/integrations/integrate-weave'
|
||||
| 'use-dify/monitor/logs'
|
||||
| 'use-dify/nodes/agent'
|
||||
| 'use-dify/nodes/answer'
|
||||
| 'use-dify/nodes/code'
|
||||
| 'use-dify/nodes/doc-extractor'
|
||||
| 'use-dify/nodes/http-request'
|
||||
| 'use-dify/nodes/ifelse'
|
||||
| 'use-dify/nodes/iteration'
|
||||
| 'use-dify/nodes/knowledge-retrieval'
|
||||
| 'use-dify/nodes/list-operator'
|
||||
| 'use-dify/nodes/llm'
|
||||
| 'use-dify/nodes/loop'
|
||||
| 'use-dify/nodes/output'
|
||||
| 'use-dify/nodes/parameter-extractor'
|
||||
| 'use-dify/nodes/question-classifier'
|
||||
| 'use-dify/nodes/template'
|
||||
| 'use-dify/nodes/tools'
|
||||
| 'use-dify/nodes/trigger/overview'
|
||||
| 'use-dify/nodes/trigger/plugin-trigger'
|
||||
| 'use-dify/nodes/trigger/schedule-trigger'
|
||||
| 'use-dify/nodes/trigger/webhook-trigger'
|
||||
| 'use-dify/nodes/user-input'
|
||||
| 'use-dify/nodes/variable-aggregator'
|
||||
| 'use-dify/nodes/variable-assigner'
|
||||
| 'use-dify/publish/README'
|
||||
| 'use-dify/publish/developing-with-apis'
|
||||
| 'use-dify/publish/publish-mcp'
|
||||
| 'use-dify/publish/webapp/chatflow-webapp'
|
||||
| 'use-dify/publish/webapp/embedding-in-websites'
|
||||
| 'use-dify/publish/webapp/web-app-access'
|
||||
| 'use-dify/publish/webapp/web-app-settings'
|
||||
| 'use-dify/publish/webapp/workflow-webapp'
|
||||
| 'use-dify/tutorials/article-reader'
|
||||
| 'use-dify/tutorials/build-ai-image-generation-app'
|
||||
| 'use-dify/tutorials/customer-service-bot'
|
||||
| 'use-dify/tutorials/simple-chatbot'
|
||||
| 'use-dify/tutorials/twitter-chatflow'
|
||||
| 'use-dify/workspace/app-management'
|
||||
| 'use-dify/workspace/model-providers'
|
||||
| 'use-dify/workspace/personal-account-management'
|
||||
| 'use-dify/workspace/plugins'
|
||||
| 'use-dify/workspace/readme'
|
||||
| 'use-dify/workspace/subscription-management'
|
||||
| 'use-dify/workspace/team-members-management'
|
||||
|
||||
// SelfHost paths
|
||||
export type SelfHostPath =
|
||||
| 'self-host/advanced-deployments/local-source-code'
|
||||
| 'self-host/advanced-deployments/start-the-frontend-docker-container'
|
||||
| 'self-host/configuration/environments'
|
||||
| 'self-host/platform-guides/bt-panel'
|
||||
| 'self-host/platform-guides/dify-premium'
|
||||
| 'self-host/quick-start/docker-compose'
|
||||
| 'self-host/quick-start/faqs'
|
||||
| 'self-host/troubleshooting/common-issues'
|
||||
| 'self-host/troubleshooting/docker-issues'
|
||||
| 'self-host/troubleshooting/integrations'
|
||||
| 'self-host/troubleshooting/storage-and-migration'
|
||||
| 'self-host/troubleshooting/weaviate-v4-migration'
|
||||
|
||||
// DevelopPlugin paths
|
||||
export type DevelopPluginPath =
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/agent-strategy-plugin'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/cheatsheet'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/creating-new-model-provider'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/datasource-plugin'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/develop-a-slack-bot-plugin'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/develop-flomo-plugin'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/develop-md-exporter'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/develop-multimodal-data-processing-tool'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/endpoint'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/tool-oauth'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/tool-plugin'
|
||||
| 'develop-plugin/dev-guides-and-walkthroughs/trigger-plugin'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/bundle'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/customizable-model'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/reverse-invocation'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/reverse-invocation-app'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/reverse-invocation-model'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/reverse-invocation-node'
|
||||
| 'develop-plugin/features-and-specs/advanced-development/reverse-invocation-tool'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/general-specifications'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/model-designing-rules'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/model-schema'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/multilingual-readme'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/persistent-storage-kv'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/plugin-info-by-manifest'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/plugin-logging'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/remote-debug-a-plugin'
|
||||
| 'develop-plugin/features-and-specs/plugin-types/tool'
|
||||
| 'develop-plugin/getting-started/cli'
|
||||
| 'develop-plugin/getting-started/getting-started-dify-plugin'
|
||||
| 'develop-plugin/publishing/faq/faq'
|
||||
| 'develop-plugin/publishing/marketplace-listing/plugin-auto-publish-pr'
|
||||
| 'develop-plugin/publishing/marketplace-listing/release-by-file'
|
||||
| 'develop-plugin/publishing/marketplace-listing/release-overview'
|
||||
| 'develop-plugin/publishing/marketplace-listing/release-to-dify-marketplace'
|
||||
| 'develop-plugin/publishing/marketplace-listing/release-to-individual-github-repo'
|
||||
| 'develop-plugin/publishing/standards/contributor-covenant-code-of-conduct'
|
||||
| 'develop-plugin/publishing/standards/privacy-protection-guidelines'
|
||||
| 'develop-plugin/publishing/standards/third-party-signature-verification'
|
||||
|
||||
// API Reference paths
|
||||
export type ApiReferencePath =
|
||||
| 'api-reference/openapi_chat.json'
|
||||
| 'api-reference/openapi_chatflow.json'
|
||||
| 'api-reference/openapi_workflow.json'
|
||||
| 'api-reference/openapi_knowledge.json'
|
||||
| 'api-reference/openapi_completion.json'
|
||||
|
||||
// Combined path without language prefix
|
||||
export type DocPathWithoutLang =
|
||||
| UseDifyPath
|
||||
| SelfHostPath
|
||||
| DevelopPluginPath
|
||||
| ApiReferencePath
|
||||
|
||||
// Full documentation path with language prefix
|
||||
export type DifyDocPath = `${DocLanguage}/${DocPathWithoutLang}`
|
||||
Loading…
Reference in New Issue
Block a user